From 0c101461e267850925218d6a6872c379f2498b16 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 28 Jul 2011 10:17:40 +1000 Subject: drm/nv40/pm: parse fan pwm divisor from vbios tables Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_perf.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index dfddb7e078a1..999bcb6a20b8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -525,6 +525,7 @@ struct nouveau_pm_engine { struct nouveau_pm_memtimings memtimings; struct nouveau_pm_temp_sensor_constants sensor_constants; struct nouveau_pm_threshold_temp threshold_temp; + u32 pwm_divisor; struct nouveau_pm_level boot; struct nouveau_pm_level *cur; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 33d03fbf00df..3d20dca08fe8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -210,6 +210,9 @@ nouveau_perf_init(struct drm_device *dev) recordlen = perf[2] + (perf[3] * perf[4]); entries = perf[5]; } + + if (version < 0x30) + pm->pwm_divisor = ROM16(perf[6]); } else { if (bios->data[bios->offset + 6] < 0x25) { legacy_perf_init(dev); @@ -283,7 +286,6 @@ nouveau_perf_init(struct drm_device *dev) perflvl->memory = ROM16(entry[11]) * 1000; else perflvl->memory = ROM16(entry[11]) * 2000; - break; case 0x25: perflvl->fanspeed = entry[4]; -- cgit v1.2.3 From 9232969e19ae7251a93ab72e405cf71e5109ec05 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 28 Jul 2011 10:40:48 +1000 Subject: drm/nv40/pm: implement first type of pwm fanspeed funcs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.h | 2 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 9 +++++++++ drivers/gpu/drm/nouveau/nv40_pm.c | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 8ac02cdd03a1..bbab7013aed1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -56,6 +56,8 @@ void nv04_pm_clock_set(struct drm_device *, void *); 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 *); +int nv40_pm_fanspeed_get(struct drm_device *); +int nv40_pm_fanspeed_set(struct drm_device *, int percent); /* nv50_pm.c */ int nv50_pm_clock_get(struct drm_device *, u32 id); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index d8831ab42bb9..06664e779792 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -292,6 +292,15 @@ 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; engine->pm.temp_get = nv40_temp_get; + switch (dev_priv->chipset) { + case 0x40: + case 0x49: + engine->pm.fanspeed_get = nv40_pm_fanspeed_get; + engine->pm.fanspeed_set = nv40_pm_fanspeed_set; + break; + default: + break; + } engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index e676b0d53478..c969bcbab547 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -346,3 +346,29 @@ resume: kfree(info); } + +int +nv40_pm_fanspeed_get(struct drm_device *dev) +{ + u32 reg = nv_rd32(dev, 0x0010f0); + if (reg & 0x80000000) { + u32 duty = (reg & 0x7fff0000) >> 16; + u32 divs = (reg & 0x00007fff); + if (divs && divs >= duty) + return ((divs - duty) * 100) / divs; + } + + return 100; +} + +int +nv40_pm_fanspeed_set(struct drm_device *dev, int percent) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + u32 divs = pm->pwm_divisor; + u32 duty = ((100 - percent) * divs) / 100; + + nv_wr32(dev, 0x0010f0, 0x80000000 | (duty << 16) | divs); + return 0; +} -- cgit v1.2.3 From 04de6a046188d86ff60b1ede974dbf580287fc98 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 28 Jul 2011 10:52:13 +1000 Subject: drm/nv41/pm: implement a second type of fanspeed pwm Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.h | 2 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 7 +++++++ drivers/gpu/drm/nouveau/nv40_pm.c | 27 +++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index bbab7013aed1..f19b0507fdfd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -58,6 +58,8 @@ void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); void nv40_pm_clocks_set(struct drm_device *, void *); int nv40_pm_fanspeed_get(struct drm_device *); int nv40_pm_fanspeed_set(struct drm_device *, int percent); +int nv41_pm_fanspeed_get(struct drm_device *); +int nv41_pm_fanspeed_set(struct drm_device *, int percent); /* nv50_pm.c */ int nv50_pm_clock_get(struct drm_device *, u32 id); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 06664e779792..0806d017d86b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -298,6 +298,13 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.fanspeed_get = nv40_pm_fanspeed_get; engine->pm.fanspeed_set = nv40_pm_fanspeed_set; break; + case 0x42: + case 0x43: + case 0x47: + case 0x4b: + engine->pm.fanspeed_get = nv41_pm_fanspeed_get; + engine->pm.fanspeed_set = nv41_pm_fanspeed_set; + break; default: break; } diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index c969bcbab547..e7660b175de6 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -372,3 +372,30 @@ nv40_pm_fanspeed_set(struct drm_device *dev, int percent) nv_wr32(dev, 0x0010f0, 0x80000000 | (duty << 16) | divs); return 0; } + +int +nv41_pm_fanspeed_get(struct drm_device *dev) +{ + u32 reg = nv_rd32(dev, 0x0015f4); + if (reg & 0x80000000) { + u32 divs = nv_rd32(dev, 0x0015f8); + u32 duty = (reg & 0x7fffffff); + if (divs && divs >= duty) + return ((divs - duty) * 100) / divs; + } + + return 100; +} + +int +nv41_pm_fanspeed_set(struct drm_device *dev, int percent) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + u32 divs = pm->pwm_divisor; + u32 duty = ((100 - percent) * divs) / 100; + + nv_wr32(dev, 0x0015f8, divs); + nv_wr32(dev, 0x0015f4, duty | 0x80000000); + return 0; +} -- cgit v1.2.3 From 771e1035b9bfdb0c3f0e34bd281d73b721a10adb Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 28 Jul 2011 11:01:21 +1000 Subject: drm/nouveau/pm: hook up fanspeed get/set if they're present Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index a539fd257921..607e4965f4ff 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -64,6 +64,12 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (perflvl == pm->cur) return 0; + if (pm->fanspeed_set && perflvl->fanspeed) { + ret = pm->fanspeed_set(dev, perflvl->fanspeed); + if (ret) + NV_ERROR(dev, "set fanspeed failed: %d\n", ret); + } + if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) { ret = pm->voltage_set(dev, perflvl->volt_min); if (ret) { @@ -161,6 +167,9 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + if (pm->fanspeed_get) + perflvl->fanspeed = pm->fanspeed_get(dev); + return 0; } -- cgit v1.2.3 From 8f27c54342dffbfbafbddd6e43f011e6cb16d285 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 11 Aug 2011 14:58:06 +1000 Subject: drm/nouveau/vdec: implement stub modules for the known engines Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 5 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 17 +++++++ drivers/gpu/drm/nouveau/nouveau_state.c | 25 ++++++++-- drivers/gpu/drm/nouveau/nv84_bsp.c | 83 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv84_vp.c | 83 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv98_crypt.c | 78 +++++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv98_ppp.c | 78 +++++++++++++++++++++++++++++++ 7 files changed, 363 insertions(+), 6 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nv84_bsp.c create mode 100644 drivers/gpu/drm/nouveau/nv84_vp.c create mode 100644 drivers/gpu/drm/nouveau/nv98_crypt.c create mode 100644 drivers/gpu/drm/nouveau/nv98_ppp.c (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 35ef5b1e3566..75ef7664f12a 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -19,9 +19,12 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ nv40_graph.o nv50_graph.o nvc0_graph.o \ nv40_grctx.o nv50_grctx.o nvc0_grctx.o \ - nv84_crypt.o \ + nv84_crypt.o nv98_crypt.o \ nva3_copy.o nvc0_copy.o \ nv31_mpeg.o nv50_mpeg.o \ + nv84_bsp.o \ + nv84_vp.o \ + nv98_ppp.o \ nv04_instmem.o nv50_instmem.o nvc0_instmem.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 \ diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 999bcb6a20b8..2c8ebf6248a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -163,6 +163,9 @@ enum nouveau_flags { #define NVOBJ_ENGINE_COPY0 3 #define NVOBJ_ENGINE_COPY1 4 #define NVOBJ_ENGINE_MPEG 5 +#define NVOBJ_ENGINE_PPP NVOBJ_ENGINE_MPEG +#define NVOBJ_ENGINE_BSP 6 +#define NVOBJ_ENGINE_VP 7 #define NVOBJ_ENGINE_DISPLAY 15 #define NVOBJ_ENGINE_NR 16 @@ -1226,6 +1229,9 @@ extern int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst); /* nv84_crypt.c */ extern int nv84_crypt_create(struct drm_device *); +/* nv98_crypt.c */ +extern int nv98_crypt_create(struct drm_device *dev); + /* nva3_copy.c */ extern int nva3_copy_create(struct drm_device *dev); @@ -1238,6 +1244,17 @@ extern int nv31_mpeg_create(struct drm_device *dev); /* nv50_mpeg.c */ extern int nv50_mpeg_create(struct drm_device *dev); +/* nv84_bsp.c */ +/* nv98_bsp.c */ +extern int nv84_bsp_create(struct drm_device *dev); + +/* nv84_vp.c */ +/* nv98_vp.c */ +extern int nv84_vp_create(struct drm_device *dev); + +/* nv98_ppp.c */ +extern int nv98_ppp_create(struct drm_device *dev); + /* nv04_instmem.c */ extern int nv04_instmem_init(struct drm_device *); extern void nv04_instmem_takedown(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 0806d017d86b..e7ba6e7c0938 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -679,6 +679,11 @@ nouveau_card_init(struct drm_device *dev) case 0xa0: nv84_crypt_create(dev); break; + case 0x98: + case 0xaa: + case 0xac: + nv98_crypt_create(dev); + break; } switch (dev_priv->card_type) { @@ -700,15 +705,25 @@ nouveau_card_init(struct drm_device *dev) break; } + if (dev_priv->chipset >= 0xa3 || dev_priv->chipset == 0x98) { + nv84_bsp_create(dev); + nv84_vp_create(dev); + nv98_ppp_create(dev); + } else + if (dev_priv->chipset >= 0x84) { + nv50_mpeg_create(dev); + nv84_bsp_create(dev); + nv84_vp_create(dev); + } else + if (dev_priv->chipset >= 0x50) { + nv50_mpeg_create(dev); + } else if (dev_priv->card_type == NV_40 || dev_priv->chipset == 0x31 || dev_priv->chipset == 0x34 || - dev_priv->chipset == 0x36) + dev_priv->chipset == 0x36) { nv31_mpeg_create(dev); - else - if (dev_priv->card_type == NV_50 && - (dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0)) - nv50_mpeg_create(dev); + } for (e = 0; e < NVOBJ_ENGINE_NR; e++) { if (dev_priv->eng[e]) { diff --git a/drivers/gpu/drm/nouveau/nv84_bsp.c b/drivers/gpu/drm/nouveau/nv84_bsp.c new file mode 100644 index 000000000000..74875739bcc0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv84_bsp.c @@ -0,0 +1,83 @@ +/* + * 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_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +/*XXX: This stub is currently used on NV98+ also, as soon as this becomes + * more than just an enable/disable stub this needs to be split out to + * nv98_bsp.c... + */ + +struct nv84_bsp_engine { + struct nouveau_exec_engine base; +}; + +static int +nv84_bsp_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00008000)) + return 0; + + nv_mask(dev, 0x000200, 0x00008000, 0x00000000); + return 0; +} + +static int +nv84_bsp_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00008000, 0x00000000); + nv_mask(dev, 0x000200, 0x00008000, 0x00008000); + return 0; +} + +static void +nv84_bsp_destroy(struct drm_device *dev, int engine) +{ + struct nv84_bsp_engine *pbsp = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, BSP); + + kfree(pbsp); +} + +int +nv84_bsp_create(struct drm_device *dev) +{ + struct nv84_bsp_engine *pbsp; + + pbsp = kzalloc(sizeof(*pbsp), GFP_KERNEL); + if (!pbsp) + return -ENOMEM; + + pbsp->base.destroy = nv84_bsp_destroy; + pbsp->base.init = nv84_bsp_init; + pbsp->base.fini = nv84_bsp_fini; + + NVOBJ_ENGINE_ADD(dev, BSP, &pbsp->base); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv84_vp.c b/drivers/gpu/drm/nouveau/nv84_vp.c new file mode 100644 index 000000000000..6570d300ab85 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv84_vp.c @@ -0,0 +1,83 @@ +/* + * 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_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +/*XXX: This stub is currently used on NV98+ also, as soon as this becomes + * more than just an enable/disable stub this needs to be split out to + * nv98_vp.c... + */ + +struct nv84_vp_engine { + struct nouveau_exec_engine base; +}; + +static int +nv84_vp_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00020000)) + return 0; + + nv_mask(dev, 0x000200, 0x00020000, 0x00000000); + return 0; +} + +static int +nv84_vp_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00020000, 0x00000000); + nv_mask(dev, 0x000200, 0x00020000, 0x00020000); + return 0; +} + +static void +nv84_vp_destroy(struct drm_device *dev, int engine) +{ + struct nv84_vp_engine *pvp = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, VP); + + kfree(pvp); +} + +int +nv84_vp_create(struct drm_device *dev) +{ + struct nv84_vp_engine *pvp; + + pvp = kzalloc(sizeof(*pvp), GFP_KERNEL); + if (!pvp) + return -ENOMEM; + + pvp->base.destroy = nv84_vp_destroy; + pvp->base.init = nv84_vp_init; + pvp->base.fini = nv84_vp_fini; + + NVOBJ_ENGINE_ADD(dev, VP, &pvp->base); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv98_crypt.c b/drivers/gpu/drm/nouveau/nv98_crypt.c new file mode 100644 index 000000000000..db94ff0a9fab --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv98_crypt.c @@ -0,0 +1,78 @@ +/* + * 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_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +struct nv98_crypt_engine { + struct nouveau_exec_engine base; +}; + +static int +nv98_crypt_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00004000)) + return 0; + + nv_mask(dev, 0x000200, 0x00004000, 0x00000000); + return 0; +} + +static int +nv98_crypt_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00004000, 0x00000000); + nv_mask(dev, 0x000200, 0x00004000, 0x00004000); + return 0; +} + +static void +nv98_crypt_destroy(struct drm_device *dev, int engine) +{ + struct nv98_crypt_engine *pcrypt = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, CRYPT); + + kfree(pcrypt); +} + +int +nv98_crypt_create(struct drm_device *dev) +{ + struct nv98_crypt_engine *pcrypt; + + pcrypt = kzalloc(sizeof(*pcrypt), GFP_KERNEL); + if (!pcrypt) + return -ENOMEM; + + pcrypt->base.destroy = nv98_crypt_destroy; + pcrypt->base.init = nv98_crypt_init; + pcrypt->base.fini = nv98_crypt_fini; + + NVOBJ_ENGINE_ADD(dev, CRYPT, &pcrypt->base); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv98_ppp.c b/drivers/gpu/drm/nouveau/nv98_ppp.c new file mode 100644 index 000000000000..a987dd6e0036 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv98_ppp.c @@ -0,0 +1,78 @@ +/* + * 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_util.h" +#include "nouveau_vm.h" +#include "nouveau_ramht.h" + +struct nv98_ppp_engine { + struct nouveau_exec_engine base; +}; + +static int +nv98_ppp_fini(struct drm_device *dev, int engine, bool suspend) +{ + if (!(nv_rd32(dev, 0x000200) & 0x00000002)) + return 0; + + nv_mask(dev, 0x000200, 0x00000002, 0x00000000); + return 0; +} + +static int +nv98_ppp_init(struct drm_device *dev, int engine) +{ + nv_mask(dev, 0x000200, 0x00000002, 0x00000000); + nv_mask(dev, 0x000200, 0x00000002, 0x00000002); + return 0; +} + +static void +nv98_ppp_destroy(struct drm_device *dev, int engine) +{ + struct nv98_ppp_engine *pppp = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, PPP); + + kfree(pppp); +} + +int +nv98_ppp_create(struct drm_device *dev) +{ + struct nv98_ppp_engine *pppp; + + pppp = kzalloc(sizeof(*pppp), GFP_KERNEL); + if (!pppp) + return -ENOMEM; + + pppp->base.destroy = nv98_ppp_destroy; + pppp->base.init = nv98_ppp_init; + pppp->base.fini = nv98_ppp_fini; + + NVOBJ_ENGINE_ADD(dev, PPP, &pppp->base); + return 0; +} -- cgit v1.2.3 From cb9fa62671ace5ac40b9924e9014cebf04b78228 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 14 Aug 2011 12:43:47 +1000 Subject: drm/nv50/pm: add support for pwm fan control Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 7 +++- drivers/gpu/drm/nouveau/nouveau_pm.h | 2 + drivers/gpu/drm/nouveau/nouveau_state.c | 4 ++ drivers/gpu/drm/nouveau/nv50_pm.c | 74 +++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 607e4965f4ff..ee2872ed60ee 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -167,8 +167,11 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } - if (pm->fanspeed_get) - perflvl->fanspeed = pm->fanspeed_get(dev); + if (pm->fanspeed_get) { + ret = pm->fanspeed_get(dev); + if (ret > 0) + perflvl->fanspeed = ret; + } return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index f19b0507fdfd..1b0bcef9ff35 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -66,6 +66,8 @@ int nv50_pm_clock_get(struct drm_device *, u32 id); void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, u32 id, int khz); void nv50_pm_clock_set(struct drm_device *, void *); +int nv50_pm_fanspeed_get(struct drm_device *); +int nv50_pm_fanspeed_set(struct drm_device *, int percent); /* nva3_pm.c */ int nva3_pm_clocks_get(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 e7ba6e7c0938..16195e9a9f91 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -386,6 +386,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv84_temp_get; else engine->pm.temp_get = nv40_temp_get; + engine->pm.fanspeed_get = nv50_pm_fanspeed_get; + engine->pm.fanspeed_set = nv50_pm_fanspeed_set; engine->vram.init = nv50_vram_init; engine->vram.takedown = nv50_vram_fini; engine->vram.get = nv50_vram_new; @@ -441,6 +443,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_get = nvc0_pm_clocks_get; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; + engine->pm.fanspeed_get = nv50_pm_fanspeed_get; + engine->pm.fanspeed_set = nv50_pm_fanspeed_set; break; case 0xd0: engine->instmem.init = nvc0_instmem_init; diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 3d5a86b98282..713c718206e3 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -144,3 +144,77 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state) kfree(state); } +struct pwm_info { + int id; + int invert; + u8 tag; + u32 ctrl; + int line; +}; + +static int +nv50_pm_fanspeed_pwm(struct drm_device *dev, struct pwm_info *pwm) +{ + struct dcb_gpio_entry *gpio; + + gpio = nouveau_bios_gpio_entry(dev, 0x09); + if (gpio) { + pwm->tag = gpio->tag; + pwm->id = (gpio->line == 9) ? 1 : 0; + pwm->invert = gpio->state[0] & 1; + pwm->ctrl = (gpio->line < 16) ? 0xe100 : 0xe28c; + pwm->line = (gpio->line & 0xf); + return 0; + } + + return -ENOENT; +} + +int +nv50_pm_fanspeed_get(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct pwm_info pwm; + int ret; + + ret = nv50_pm_fanspeed_pwm(dev, &pwm); + if (ret) + return ret; + + if (nv_rd32(dev, pwm.ctrl) & (0x00000001 << pwm.line)) { + u32 divs = nv_rd32(dev, 0x00e114 + (pwm.id * 8)); + u32 duty = nv_rd32(dev, 0x00e118 + (pwm.id * 8)); + if (divs) { + divs = max(divs, duty); + if (pwm.invert) + duty = divs - duty; + return (duty * 100) / divs; + } + + return 0; + } + + return pgpio->get(dev, pwm.tag) * 100; +} + +int +nv50_pm_fanspeed_set(struct drm_device *dev, int percent) +{ + struct pwm_info pwm; + u32 divs, duty; + int ret; + + ret = nv50_pm_fanspeed_pwm(dev, &pwm); + if (ret) + return ret; + + divs = nv_rd32(dev, 0x00e114 + (pwm.id * 8)); + duty = ((divs * percent) + 99) / 100; + if (pwm.invert) + duty = divs - duty; + + nv_mask(dev, pwm.ctrl, 0x00010001 << pwm.line, 0x00000001 << pwm.line); + nv_wr32(dev, 0x00e118 + (pwm.id * 8), 0x80000000 | duty); + return 0; +} -- cgit v1.2.3 From 11b7d895216f7f954c6cfa0c23b76dccb7a890c1 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Mon, 15 Aug 2011 11:10:30 +1000 Subject: drm/nouveau/pm: manual pwm fanspeed management for nv40+ boards Exposes the following sysfs entries: - fan0_input: read the rotational speed of the fan (poll a bit during 250ms) - pwm0: set the pwm duty cycle - pwm0_min/max: set the minimum/maximum pwm value v2 (Ben Skeggs): - nv50 pwm controller code removed in favour of other more complete code - FAN_RPM -> FAN_SENSE - merged FAN_SENSE readout into common code, not at all nv50-specific - protected fanspeed changes with perflvl_wr - formatting tidying - added some comments where things are shaky v3 (Martin Peres) - ensure duty min/max from thermal table are sane Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_bios.h | 2 + drivers/gpu/drm/nouveau/nouveau_drv.h | 6 + drivers/gpu/drm/nouveau/nouveau_pm.c | 234 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nouveau_temp.c | 16 +++ 4 files changed, 252 insertions(+), 6 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 8adb69e4a6b1..3f36c7074d4f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -58,6 +58,8 @@ struct dcb_i2c_entry { enum dcb_gpio_tag { DCB_GPIO_TVDAC0 = 0xc, DCB_GPIO_TVDAC1 = 0x2d, + DCB_GPIO_PWM_FAN = 0x9, + DCB_GPIO_FAN_SENSE = 0x3d, }; struct dcb_gpio_entry { diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 2c8ebf6248a0..432be658060d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -521,6 +521,11 @@ struct nouveau_pm_memtimings { int nr_timing; }; +struct nouveau_pm_fan { + u32 min_duty; + u32 max_duty; +}; + struct nouveau_pm_engine { struct nouveau_pm_voltage voltage; struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; @@ -528,6 +533,7 @@ struct nouveau_pm_engine { struct nouveau_pm_memtimings memtimings; struct nouveau_pm_temp_sensor_constants sensor_constants; struct nouveau_pm_threshold_temp threshold_temp; + struct nouveau_pm_fan fan; u32 pwm_divisor; struct nouveau_pm_level boot; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index ee2872ed60ee..23b297fd6f61 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -64,6 +64,10 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (perflvl == pm->cur) return 0; + /*XXX: not on all boards, we should control based on temperature + * on recent boards.. or maybe on some other factor we don't + * know about? + */ if (pm->fanspeed_set && perflvl->fanspeed) { ret = pm->fanspeed_set(dev, perflvl->fanspeed); if (ret) @@ -424,6 +428,176 @@ static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, nouveau_hwmon_show_update_rate, NULL, 0); +static ssize_t +nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct dcb_gpio_entry *gpio; + u32 cycles, cur, prev; + u64 start; + + gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE); + if (!gpio) + return -ENODEV; + + /* Monitor the GPIO input 0x3b for 250ms. + * When the fan spins, it changes the value of GPIO FAN_SENSE. + * We get 4 changes (0 -> 1 -> 0 -> 1 -> [...]) per complete rotation. + */ + start = ptimer->read(dev); + prev = pgpio->get(dev, DCB_GPIO_FAN_SENSE); + cycles = 0; + do { + cur = pgpio->get(dev, DCB_GPIO_FAN_SENSE); + if (prev != cur) { + cycles++; + prev = cur; + } + + usleep_range(500, 1000); /* supports 0 < rpm < 7500 */ + } while (ptimer->read(dev) - start < 250000000); + + /* interpolate to get rpm */ + return sprintf(buf, "%i\n", cycles / 4 * 4 * 60); +} +static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input, + NULL, 0); + +static ssize_t +nouveau_hwmon_get_pwm0(struct device *d, struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + int ret = -ENODEV; + + if (pm->fanspeed_get) + ret = pm->fanspeed_get(dev); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t +nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + int ret = -ENODEV; + long value; + + if (nouveau_perflvl_wr != 7777) + return -EPERM; + + if (strict_strtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + if (value < pm->fan.min_duty) + value = pm->fan.min_duty; + if (value > pm->fan.max_duty) + value = pm->fan.max_duty; + + if (pm->fanspeed_set) + ret = pm->fanspeed_set(dev, value); + if (ret) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm0, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm0, + nouveau_hwmon_set_pwm0, 0); + +static ssize_t +nouveau_hwmon_get_pwm0_min(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + + return sprintf(buf, "%i\n", pm->fan.min_duty); +} + +static ssize_t +nouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + long value; + + if (strict_strtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + if (value < 0) + value = 0; + + if (pm->fan.max_duty - value < 10) + value = pm->fan.max_duty - 10; + + if (value < 10) + pm->fan.min_duty = 10; + else + pm->fan.min_duty = value; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm0_min, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm0_min, + nouveau_hwmon_set_pwm0_min, 0); + +static ssize_t +nouveau_hwmon_get_pwm0_max(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + + return sprintf(buf, "%i\n", pm->fan.max_duty); +} + +static ssize_t +nouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + long value; + + if (strict_strtol(buf, 10, &value) == -EINVAL) + return -EINVAL; + + if (value < 0) + value = 0; + + if (value - pm->fan.min_duty < 10) + value = pm->fan.min_duty + 10; + + if (value > 100) + pm->fan.max_duty = 100; + else + pm->fan.max_duty = value; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm0_max, S_IRUGO | S_IWUSR, + nouveau_hwmon_get_pwm0_max, + nouveau_hwmon_set_pwm0_max, 0); + static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -432,10 +606,26 @@ static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_update_rate.dev_attr.attr, NULL }; +static struct attribute *hwmon_fan_rpm_attributes[] = { + &sensor_dev_attr_fan0_input.dev_attr.attr, + NULL +}; +static struct attribute *hwmon_pwm_fan_attributes[] = { + &sensor_dev_attr_pwm0.dev_attr.attr, + &sensor_dev_attr_pwm0_min.dev_attr.attr, + &sensor_dev_attr_pwm0_max.dev_attr.attr, + NULL +}; static const struct attribute_group hwmon_attrgroup = { .attrs = hwmon_attributes, }; +static const struct attribute_group hwmon_fan_rpm_attrgroup = { + .attrs = hwmon_fan_rpm_attributes, +}; +static const struct attribute_group hwmon_pwm_fan_attrgroup = { + .attrs = hwmon_pwm_fan_attributes, +}; #endif static int @@ -445,7 +635,7 @@ nouveau_hwmon_init(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *hwmon_dev; - int ret; + int ret = 0; if (!pm->temp_get) return -ENODEV; @@ -458,17 +648,46 @@ nouveau_hwmon_init(struct drm_device *dev) return ret; } dev_set_drvdata(hwmon_dev, dev); + + /* default sysfs entries */ ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); if (ret) { - NV_ERROR(dev, - "Unable to create hwmon sysfs file: %d\n", ret); - hwmon_device_unregister(hwmon_dev); - return ret; + if (ret) + goto error; + } + + /* if the card has a pwm fan */ + /*XXX: incorrect, need better detection for this, some boards have + * the gpio entries for pwm fan control even when there's no + * actual fan connected to it... therm table? */ + if (pm->fanspeed_get && pm->fanspeed_get(dev) >= 0) { + ret = sysfs_create_group(&dev->pdev->dev.kobj, + &hwmon_pwm_fan_attrgroup); + if (ret) + goto error; + } + + /* if the card can read the fan rpm */ + if (nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE)) { + ret = sysfs_create_group(&dev->pdev->dev.kobj, + &hwmon_fan_rpm_attrgroup); + if (ret) + goto error; } pm->hwmon = hwmon_dev; -#endif + return 0; + +error: + NV_ERROR(dev, "Unable to create some hwmon sysfs files: %d\n", ret); + hwmon_device_unregister(hwmon_dev); + pm->hwmon = NULL; + return ret; +#else + pm->hwmon = NULL; + return 0; +#endif } static void @@ -480,6 +699,9 @@ nouveau_hwmon_fini(struct drm_device *dev) if (pm->hwmon) { sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); + sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup); + sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup); + hwmon_device_unregister(pm->hwmon); } #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 5a46446dd5a8..97c172cae3bc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -55,6 +55,10 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) temps->down_clock = 100; temps->fan_boost = 90; + /* Set the default range for the pwm fan */ + pm->fan.min_duty = 30; + pm->fan.max_duty = 100; + /* Set the known default values to setup the temperature sensor */ if (dev_priv->card_type >= NV_40) { switch (dev_priv->chipset) { @@ -156,11 +160,23 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) case 0x13: sensor->slope_div = value; break; + case 0x22: + pm->fan.min_duty = value & 0xff; + pm->fan.max_duty = (value & 0xff00) >> 8; + break; } temp += recordlen; } nouveau_temp_safety_checks(dev); + + /* check the fan min/max settings */ + if (pm->fan.min_duty < 10) + pm->fan.min_duty = 10; + if (pm->fan.max_duty > 100) + pm->fan.max_duty = 100; + if (pm->fan.max_duty < pm->fan.min_duty) + pm->fan.max_duty = pm->fan.min_duty; } static int -- cgit v1.2.3 From 3f8e11e4b606a050007cd1020d59e7b74c68d088 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 15 Aug 2011 16:13:34 +1000 Subject: drm/nv50/pm: mostly nailed down fan pwm frequency selection Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_perf.c | 5 ++--- drivers/gpu/drm/nouveau/nouveau_temp.c | 3 +++ drivers/gpu/drm/nouveau/nv50_pm.c | 12 +++++++++++- 4 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 432be658060d..251eaf87fa28 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -524,6 +524,7 @@ struct nouveau_pm_memtimings { struct nouveau_pm_fan { u32 min_duty; u32 max_duty; + u32 pwm_freq; }; struct nouveau_pm_engine { diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 3d20dca08fe8..6ea57c91162b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -206,13 +206,12 @@ nouveau_perf_init(struct drm_device *dev) if (version < 0x40) { recordlen = perf[3] + (perf[4] * perf[5]); entries = perf[2]; + + pm->pwm_divisor = ROM16(perf[6]); } else { recordlen = perf[2] + (perf[3] * perf[4]); entries = perf[5]; } - - if (version < 0x30) - pm->pwm_divisor = ROM16(perf[6]); } else { if (bios->data[bios->offset + 6] < 0x25) { legacy_perf_init(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 97c172cae3bc..6364e4c46124 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -164,6 +164,9 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) pm->fan.min_duty = value & 0xff; pm->fan.max_duty = (value & 0xff00) >> 8; break; + case 0x26: + pm->fan.pwm_freq = value; + break; } temp += recordlen; } diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 713c718206e3..0cbf538b6e85 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -201,6 +201,8 @@ nv50_pm_fanspeed_get(struct drm_device *dev) int nv50_pm_fanspeed_set(struct drm_device *dev, int percent) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct pwm_info pwm; u32 divs, duty; int ret; @@ -209,12 +211,20 @@ nv50_pm_fanspeed_set(struct drm_device *dev, int percent) if (ret) return ret; - divs = nv_rd32(dev, 0x00e114 + (pwm.id * 8)); + divs = pm->pwm_divisor; + if (pm->fan.pwm_freq) { + /*XXX: PNVIO clock more than likely... */ + divs = 1350000 / pm->fan.pwm_freq; + if (dev_priv->chipset < 0xa3) + divs /= 4; + } + duty = ((divs * percent) + 99) / 100; if (pwm.invert) duty = divs - duty; nv_mask(dev, pwm.ctrl, 0x00010001 << pwm.line, 0x00000001 << pwm.line); + nv_wr32(dev, 0x00e114 + (pwm.id * 8), divs); nv_wr32(dev, 0x00e118 + (pwm.id * 8), 0x80000000 | duty); return 0; } -- cgit v1.2.3 From 85a2a365216e8e4eccf826e7dcc06c6298ab5fc1 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 13 Sep 2011 05:32:43 +1000 Subject: drm/nouveau/gpio: remove invert flag, use state[] everywhere Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 6 ++++-- drivers/gpu/drm/nouveau/nouveau_bios.h | 1 - drivers/gpu/drm/nouveau/nv10_gpio.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 5fc201b49d30..41899eccf570 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -5834,7 +5834,8 @@ parse_dcb_gpio_table(struct nvbios *bios) e = new_gpio_entry(bios); e->tag = DCB_GPIO_TVDAC0; e->line = tvdac_gpio[1] >> 4; - e->invert = tvdac_gpio[0] & 2; + e->state[0] = !!(tvdac_gpio[0] & 2); + e->state[1] = !e->state[0]; } goto no_table; @@ -5858,7 +5859,8 @@ parse_dcb_gpio_table(struct nvbios *bios) } e->line = (e->entry & 0x001f); - e->invert = ((e->entry & 0xf800) >> 11) != 4; + e->state[0] = ((e->entry & 0xf800) >> 11) != 4; + e->state[1] = !e->state[0]; } else { e->entry = ROM32(entry[0]); e->tag = (e->entry & 0x0000ff00) >> 8; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 3f36c7074d4f..a1e31ea2ecbf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -65,7 +65,6 @@ enum dcb_gpio_tag { struct dcb_gpio_entry { enum dcb_gpio_tag tag; int line; - bool invert; uint32_t entry; uint8_t state_default; uint8_t state[2]; diff --git a/drivers/gpu/drm/nouveau/nv10_gpio.c b/drivers/gpu/drm/nouveau/nv10_gpio.c index 007fc29e2f86..748c9f739116 100644 --- a/drivers/gpu/drm/nouveau/nv10_gpio.c +++ b/drivers/gpu/drm/nouveau/nv10_gpio.c @@ -68,7 +68,7 @@ nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) value = NVReadCRTC(dev, 0, reg) >> shift; - return (ent->invert ? 1 : 0) ^ (value & 1); + return (value & 1) == ent->state[1]; } int @@ -83,7 +83,7 @@ nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) if (!get_gpio_location(ent, ®, &shift, &mask)) return -ENODEV; - value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift; + value = ent->state[state & 1] << shift; mask = ~(mask << shift); NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask)); -- cgit v1.2.3 From a175094cd8f3d46060d8e3510bdca57eb2369a86 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 17 Sep 2011 01:42:12 +1000 Subject: drm/nouveau/pm: introduce generic handler for on-chip fan controller The handling of the internal pwm fan controller is similar enough between current chipsets that it makes sense to share the logic, and bugfixes :) No hw backends converted yet, will automatically fall-through to the "old" per-chipset fanspeed hooks for now. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + drivers/gpu/drm/nouveau/nouveau_pm.c | 94 +++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 16 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 251eaf87fa28..ea4009eaab20 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -554,6 +554,8 @@ struct nouveau_pm_engine { int (*voltage_get)(struct drm_device *); int (*voltage_set)(struct drm_device *, int voltage); + int (*pwm_get)(struct drm_device *, struct dcb_gpio_entry*, u32*, u32*); + int (*pwm_set)(struct drm_device *, struct dcb_gpio_entry*, u32, u32); int (*fanspeed_get)(struct drm_device *); int (*fanspeed_set)(struct drm_device *, int fanspeed); int (*temp_get)(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 23b297fd6f61..b94364dbe352 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -34,6 +34,74 @@ #include #include +static int +nouveau_pwmfan_get(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct dcb_gpio_entry *gpio; + u32 divs, duty; + int ret; + + if (!pm->pwm_get) { + if (pm->fanspeed_get) + return pm->fanspeed_get(dev); + return -ENODEV; + } + + gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN); + if (gpio) { + ret = pm->pwm_get(dev, gpio, &divs, &duty); + if (ret == 0) { + divs = max(divs, duty); + if (dev_priv->card_type <= NV_40 || + (gpio->state[0] & 1)) + duty = divs - duty; + return (duty * 100) / divs; + } + + return pgpio->get(dev, gpio->tag) * 100; + } + + return -ENODEV; +} + +static int +nouveau_pwmfan_set(struct drm_device *dev, int percent) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct dcb_gpio_entry *gpio; + u32 divs, duty; + + if (!pm->pwm_set) { + if (pm->fanspeed_set) + return pm->fanspeed_set(dev, percent); + return -ENODEV; + } + + gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN); + if (gpio) { + divs = pm->pwm_divisor; + if (pm->fan.pwm_freq) { + /*XXX: PNVIO clock more than likely... */ + divs = 135000 / pm->fan.pwm_freq; + if (dev_priv->chipset < 0xa3) + divs /= 4; + } + + duty = ((divs * percent) + 99) / 100; + if (dev_priv->card_type <= NV_40 || + (gpio->state[0] & 1)) + duty = divs - duty; + + return pm->pwm_set(dev, gpio, divs, duty); + } + + return -ENODEV; +} + static int nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl, u8 id, u32 khz) @@ -68,9 +136,9 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) * on recent boards.. or maybe on some other factor we don't * know about? */ - if (pm->fanspeed_set && perflvl->fanspeed) { - ret = pm->fanspeed_set(dev, perflvl->fanspeed); - if (ret) + if (perflvl->fanspeed) { + ret = nouveau_pwmfan_set(dev, perflvl->fanspeed); + if (ret && ret != -ENODEV) NV_ERROR(dev, "set fanspeed failed: %d\n", ret); } @@ -171,11 +239,9 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } - if (pm->fanspeed_get) { - ret = pm->fanspeed_get(dev); - if (ret > 0) - perflvl->fanspeed = ret; - } + ret = nouveau_pwmfan_get(dev); + if (ret > 0) + perflvl->fanspeed = ret; return 0; } @@ -471,12 +537,9 @@ static ssize_t nouveau_hwmon_get_pwm0(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - int ret = -ENODEV; + int ret; - if (pm->fanspeed_get) - ret = pm->fanspeed_get(dev); + ret = nouveau_pwmfan_get(dev); if (ret < 0) return ret; @@ -504,8 +567,7 @@ nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a, if (value > pm->fan.max_duty) value = pm->fan.max_duty; - if (pm->fanspeed_set) - ret = pm->fanspeed_set(dev, value); + ret = nouveau_pwmfan_set(dev, value); if (ret) return ret; @@ -660,7 +722,7 @@ nouveau_hwmon_init(struct drm_device *dev) /*XXX: incorrect, need better detection for this, some boards have * the gpio entries for pwm fan control even when there's no * actual fan connected to it... therm table? */ - if (pm->fanspeed_get && pm->fanspeed_get(dev) >= 0) { + if (nouveau_pwmfan_get(dev) >= 0) { ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup); if (ret) -- cgit v1.2.3 From 5a4267ab14b392bdf43893c6175b045b5f85d53d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 17 Sep 2011 02:01:24 +1000 Subject: drm/nv50/pm: convert to new fanspeed pwm controller hooks Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.h | 4 +- drivers/gpu/drm/nouveau/nouveau_state.c | 8 +-- drivers/gpu/drm/nouveau/nv50_pm.c | 94 ++++++++++++--------------------- 3 files changed, 39 insertions(+), 67 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 1b0bcef9ff35..2be384a922b3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -66,8 +66,8 @@ int nv50_pm_clock_get(struct drm_device *, u32 id); void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, u32 id, int khz); void nv50_pm_clock_set(struct drm_device *, void *); -int nv50_pm_fanspeed_get(struct drm_device *); -int nv50_pm_fanspeed_set(struct drm_device *, int percent); +int nv50_pm_pwm_get(struct drm_device *, struct dcb_gpio_entry *, u32*, u32*); +int nv50_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); /* nva3_pm.c */ int nva3_pm_clocks_get(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 16195e9a9f91..2028a393a900 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -386,8 +386,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv84_temp_get; else engine->pm.temp_get = nv40_temp_get; - engine->pm.fanspeed_get = nv50_pm_fanspeed_get; - engine->pm.fanspeed_set = nv50_pm_fanspeed_set; + engine->pm.pwm_get = nv50_pm_pwm_get; + engine->pm.pwm_set = nv50_pm_pwm_set; engine->vram.init = nv50_vram_init; engine->vram.takedown = nv50_vram_fini; engine->vram.get = nv50_vram_new; @@ -443,8 +443,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_get = nvc0_pm_clocks_get; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; - engine->pm.fanspeed_get = nv50_pm_fanspeed_get; - engine->pm.fanspeed_set = nv50_pm_fanspeed_set; + engine->pm.pwm_get = nv50_pm_pwm_get; + engine->pm.pwm_set = nv50_pm_pwm_set; break; case 0xd0: engine->instmem.init = nvc0_instmem_init; diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 0cbf538b6e85..8a56880e4e71 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -144,87 +144,59 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state) kfree(state); } -struct pwm_info { - int id; - int invert; - u8 tag; - u32 ctrl; - int line; -}; - static int -nv50_pm_fanspeed_pwm(struct drm_device *dev, struct pwm_info *pwm) +pwm_info(struct drm_device *dev, struct dcb_gpio_entry *gpio, + int *ctrl, int *line, int *indx) { - struct dcb_gpio_entry *gpio; - - gpio = nouveau_bios_gpio_entry(dev, 0x09); - if (gpio) { - pwm->tag = gpio->tag; - pwm->id = (gpio->line == 9) ? 1 : 0; - pwm->invert = gpio->state[0] & 1; - pwm->ctrl = (gpio->line < 16) ? 0xe100 : 0xe28c; - pwm->line = (gpio->line & 0xf); - return 0; + if (gpio->line == 0x04) { + *ctrl = 0x00e100; + *line = 4; + *indx = 0; + } else + if (gpio->line == 0x09) { + *ctrl = 0x00e100; + *line = 9; + *indx = 1; + } else + if (gpio->line == 0x10) { + *ctrl = 0x00e28c; + *line = 0; + *indx = 0; + } else { + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", gpio->line); + return -ENODEV; } - return -ENOENT; + return 0; } int -nv50_pm_fanspeed_get(struct drm_device *dev) +nv50_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, + u32 *divs, u32 *duty) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct pwm_info pwm; - int ret; - - ret = nv50_pm_fanspeed_pwm(dev, &pwm); + int ctrl, line, id, ret = pwm_info(dev, gpio, &ctrl, &line, &id); if (ret) return ret; - if (nv_rd32(dev, pwm.ctrl) & (0x00000001 << pwm.line)) { - u32 divs = nv_rd32(dev, 0x00e114 + (pwm.id * 8)); - u32 duty = nv_rd32(dev, 0x00e118 + (pwm.id * 8)); - if (divs) { - divs = max(divs, duty); - if (pwm.invert) - duty = divs - duty; - return (duty * 100) / divs; - } - + if (nv_rd32(dev, ctrl) & (1 << line)) { + *divs = nv_rd32(dev, 0x00e114 + (id * 8)); + *duty = nv_rd32(dev, 0x00e118 + (id * 8)); return 0; } - return pgpio->get(dev, pwm.tag) * 100; + return -EINVAL; } int -nv50_pm_fanspeed_set(struct drm_device *dev, int percent) +nv50_pm_pwm_set(struct drm_device *dev, struct dcb_gpio_entry *gpio, + u32 divs, u32 duty) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct pwm_info pwm; - u32 divs, duty; - int ret; - - ret = nv50_pm_fanspeed_pwm(dev, &pwm); + int ctrl, line, id, ret = pwm_info(dev, gpio, &ctrl, &line, &id); if (ret) return ret; - divs = pm->pwm_divisor; - if (pm->fan.pwm_freq) { - /*XXX: PNVIO clock more than likely... */ - divs = 1350000 / pm->fan.pwm_freq; - if (dev_priv->chipset < 0xa3) - divs /= 4; - } - - duty = ((divs * percent) + 99) / 100; - if (pwm.invert) - duty = divs - duty; - - nv_mask(dev, pwm.ctrl, 0x00010001 << pwm.line, 0x00000001 << pwm.line); - nv_wr32(dev, 0x00e114 + (pwm.id * 8), divs); - nv_wr32(dev, 0x00e118 + (pwm.id * 8), 0x80000000 | duty); + nv_mask(dev, ctrl, 0x00010001 << line, 0x00000001 << line); + nv_wr32(dev, 0x00e114 + (id * 8), divs); + nv_wr32(dev, 0x00e118 + (id * 8), duty | 0x80000000); return 0; } -- cgit v1.2.3 From 693461801464eb65eb779261b3d9d80dc9131f81 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 17 Sep 2011 02:11:39 +1000 Subject: drm/nv40/pm: convert to new pwm hooks, also fixing pwm type detection A NV49 appeared a while back that was using the "nv41 style" pwm registers, rather than the "nv40 style" ones my board is using. This disproves the previous theory that the pwm controller choice is chipset-specific. So, after looking at a bunch of vbios images it appears that the next viable theory is that we should select the pwm controller to use based on the gpio line the fan is tied to, just like we do on nv50. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.h | 6 +-- drivers/gpu/drm/nouveau/nouveau_state.c | 18 +-------- drivers/gpu/drm/nouveau/nv40_pm.c | 72 +++++++++++++++------------------ 3 files changed, 36 insertions(+), 60 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 2be384a922b3..5c87afde82d6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -56,10 +56,8 @@ void nv04_pm_clock_set(struct drm_device *, void *); 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 *); -int nv40_pm_fanspeed_get(struct drm_device *); -int nv40_pm_fanspeed_set(struct drm_device *, int percent); -int nv41_pm_fanspeed_get(struct drm_device *); -int nv41_pm_fanspeed_set(struct drm_device *, int percent); +int nv40_pm_pwm_get(struct drm_device *, struct dcb_gpio_entry *, u32*, u32*); +int nv40_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); /* nv50_pm.c */ int nv50_pm_clock_get(struct drm_device *, u32 id); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 2028a393a900..3d5cf6bb1c74 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -292,22 +292,8 @@ 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; engine->pm.temp_get = nv40_temp_get; - switch (dev_priv->chipset) { - case 0x40: - case 0x49: - engine->pm.fanspeed_get = nv40_pm_fanspeed_get; - engine->pm.fanspeed_set = nv40_pm_fanspeed_set; - break; - case 0x42: - case 0x43: - case 0x47: - case 0x4b: - engine->pm.fanspeed_get = nv41_pm_fanspeed_get; - engine->pm.fanspeed_set = nv41_pm_fanspeed_set; - break; - default: - break; - } + engine->pm.pwm_get = nv40_pm_pwm_get; + engine->pm.pwm_set = nv40_pm_pwm_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index e7660b175de6..02d5be8bff1b 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -348,54 +348,46 @@ resume: } int -nv40_pm_fanspeed_get(struct drm_device *dev) +nv40_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, + u32 *divs, u32 *duty) { - u32 reg = nv_rd32(dev, 0x0010f0); - if (reg & 0x80000000) { - u32 duty = (reg & 0x7fff0000) >> 16; - u32 divs = (reg & 0x00007fff); - if (divs && divs >= duty) - return ((divs - duty) * 100) / divs; + if (gpio->line == 2) { + u32 reg = nv_rd32(dev, 0x0010f0); + if (reg & 0x80000000) { + *duty = (reg & 0x7fff0000) >> 16; + *divs = (reg & 0x00007fff); + return 0; + } + } else + if (gpio->line == 9) { + u32 reg = nv_rd32(dev, 0x0015f4); + if (reg & 0x80000000) { + *divs = nv_rd32(dev, 0x0015f8); + *duty = (reg & 0x7fffffff); + return 0; + } + } else { + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", gpio->line); + return -ENODEV; } - return 100; + return -EINVAL; } int -nv40_pm_fanspeed_set(struct drm_device *dev, int percent) +nv40_pm_pwm_set(struct drm_device *dev, struct dcb_gpio_entry *gpio, + u32 divs, u32 duty) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - u32 divs = pm->pwm_divisor; - u32 duty = ((100 - percent) * divs) / 100; - - nv_wr32(dev, 0x0010f0, 0x80000000 | (duty << 16) | divs); - return 0; -} - -int -nv41_pm_fanspeed_get(struct drm_device *dev) -{ - u32 reg = nv_rd32(dev, 0x0015f4); - if (reg & 0x80000000) { - u32 divs = nv_rd32(dev, 0x0015f8); - u32 duty = (reg & 0x7fffffff); - if (divs && divs >= duty) - return ((divs - duty) * 100) / divs; + if (gpio->line == 2) { + nv_wr32(dev, 0x0010f0, 0x80000000 | (duty << 16) | divs); + } else + if (gpio->line == 9) { + nv_wr32(dev, 0x0015f8, divs); + nv_wr32(dev, 0x0015f4, duty | 0x80000000); + } else { + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", gpio->line); + return -ENODEV; } - return 100; -} - -int -nv41_pm_fanspeed_set(struct drm_device *dev, int percent) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - u32 divs = pm->pwm_divisor; - u32 duty = ((100 - percent) * divs) / 100; - - nv_wr32(dev, 0x0015f8, divs); - nv_wr32(dev, 0x0015f4, duty | 0x80000000); return 0; } -- cgit v1.2.3 From 1e05415733b0d4668fbce92856fafabfa1a33333 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 17 Sep 2011 06:38:56 +1000 Subject: drm/nouveau/pm: remove defunct fanspeed_set/get from pm table Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 -- drivers/gpu/drm/nouveau/nouveau_pm.c | 12 +++--------- 2 files changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index ea4009eaab20..48483504edb4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -556,8 +556,6 @@ struct nouveau_pm_engine { int (*voltage_set)(struct drm_device *, int voltage); int (*pwm_get)(struct drm_device *, struct dcb_gpio_entry*, u32*, u32*); int (*pwm_set)(struct drm_device *, struct dcb_gpio_entry*, u32, u32); - int (*fanspeed_get)(struct drm_device *); - int (*fanspeed_set)(struct drm_device *, int fanspeed); int (*temp_get)(struct drm_device *); }; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index b94364dbe352..3ebc3f575c58 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -40,15 +40,12 @@ nouveau_pwmfan_get(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct dcb_gpio_entry *gpio; + struct dcb_gpio_entry *gpio = NULL; u32 divs, duty; int ret; - if (!pm->pwm_get) { - if (pm->fanspeed_get) - return pm->fanspeed_get(dev); + if (!pm->pwm_get) return -ENODEV; - } gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN); if (gpio) { @@ -75,11 +72,8 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) struct dcb_gpio_entry *gpio; u32 divs, duty; - if (!pm->pwm_set) { - if (pm->fanspeed_set) - return pm->fanspeed_set(dev, percent); + if (!pm->pwm_set) return -ENODEV; - } gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN); if (gpio) { -- cgit v1.2.3 From 35bb5089cc74e6d64cf0171b55c93e1bf8b8198d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 27 Sep 2011 10:31:36 +1000 Subject: drm/nv50/pm: s/unk05/vdec/ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 - drivers/gpu/drm/nouveau/nouveau_perf.c | 3 +-- drivers/gpu/drm/nouveau/nouveau_pm.c | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 48483504edb4..880fdd183bf7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -487,7 +487,6 @@ struct nouveau_pm_level { u32 copy; u32 daemon; u32 vdec; - u32 unk05; /* nv50:nva3, roughly.. */ u32 unka0; /* nva3:nvc0 */ u32 hub01; /* nvc0- */ u32 hub06; /* nvc0- */ diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 6ea57c91162b..da584e3a8f6a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -301,8 +301,7 @@ nouveau_perf_init(struct drm_device *dev) perflvl->core = ROM16(entry[8]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; - /*XXX: confirm on 0x35 */ - perflvl->unk05 = ROM16(entry[16]) * 1000; + perflvl->vdec = ROM16(entry[16]) * 1000; break; case 0x40: #define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000 diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 3ebc3f575c58..ea620d8de007 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -154,7 +154,7 @@ 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); + nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->vdec); } pm->cur = perflvl; @@ -222,7 +222,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) ret = pm->clock_get(dev, PLL_UNK05); if (ret > 0) - perflvl->unk05 = ret; + perflvl->vdec = ret; } if (pm->voltage.supported && pm->voltage_get) { -- cgit v1.2.3 From 25575b414c2137a16b313bdfdeab570b70080f37 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 5 Oct 2011 11:05:07 +1000 Subject: drm/nouveau/hdmi: build ELD from EDID, notify audio driver of its presence Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 3 + drivers/gpu/drm/nouveau/nouveau_hdmi.c | 103 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv50_sor.c | 4 ++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/nouveau/nouveau_hdmi.c (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 75ef7664f12a..cdd9c3d0914d 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -9,7 +9,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \ nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ - nouveau_dp.o nouveau_ramht.o \ + nouveau_hdmi.o nouveau_dp.o nouveau_ramht.o \ nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \ nouveau_mm.o nouveau_vm.o \ nv04_timer.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 880fdd183bf7..c99490d6ee7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1109,6 +1109,9 @@ int nouveau_ttm_global_init(struct drm_nouveau_private *); void nouveau_ttm_global_release(struct drm_nouveau_private *); int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); +/* nouveau_hdmi.c */ +void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); + /* nouveau_dp.c */ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c new file mode 100644 index 000000000000..489a2418a227 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c @@ -0,0 +1,103 @@ +/* + * 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" + +static bool +hdmi_sor(struct drm_encoder *encoder) +{ + struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; + if (dev_priv->chipset < 0xa3) + return false; + return true; +} + +static void +nouveau_audio_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + int or = nv_encoder->or * 0x800; + + if (hdmi_sor(encoder)) { + nv_mask(dev, 0x61c448 + or, 0x00000003, 0x00000000); + } +} + +static void +nouveau_audio_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + u32 or = nv_encoder->or * 0x800; + int i; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_monitor_audio(nv_connector->edid)) { + nouveau_audio_disconnect(encoder); + return; + } + + if (hdmi_sor(encoder)) { + nv_mask(dev, 0x61c448 + or, 0x00000001, 0x00000001); + + drm_edid_to_eld(&nv_connector->base, nv_connector->edid); + if (nv_connector->base.eld[0]) { + u8 *eld = nv_connector->base.eld; + for (i = 0; i < eld[2] * 4; i++) + nv_wr32(dev, 0x61c440 + or, (i << 8) | eld[i]); + for (i = eld[2] * 4; i < 0x60; i++) + nv_wr32(dev, 0x61c440 + or, (i << 8) | 0x00); + nv_mask(dev, 0x61c448 + or, 0x00000002, 0x00000002); + } + } +} + +static void +nouveau_hdmi_disconnect(struct drm_encoder *encoder) +{ + nouveau_audio_disconnect(encoder); +} + +void +nouveau_hdmi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!mode || !nv_connector || !nv_connector->edid || + !drm_detect_hdmi_monitor(nv_connector->edid)) { + nouveau_hdmi_disconnect(encoder); + return; + } + + nouveau_audio_mode_set(encoder, mode); +} diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index 2633aa8554eb..2a638aeb5061 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -60,6 +60,8 @@ nv50_sor_disconnect(struct drm_encoder *encoder) BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING (evo, 0); + nouveau_hdmi_mode_set(encoder, NULL); + nv_encoder->crtc = NULL; nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; } @@ -203,6 +205,8 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, mode_ctl = 0x0500; } else mode_ctl = 0x0200; + + nouveau_hdmi_mode_set(encoder, mode); break; case OUTPUT_DP: nv_connector = nouveau_encoder_connector_get(nv_encoder); -- cgit v1.2.3 From 52c7bcdb6749a3920739640ca791e1f741f139d0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 5 Oct 2011 14:52:02 +1000 Subject: drm/nouveau/hdmi: add hdmi register accessors to handle hdmi block move Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_hdmi.c | 33 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nv50_sor.c | 6 +++--- 2 files changed, 35 insertions(+), 4 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c index 489a2418a227..d8540a64a8d3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c +++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c @@ -26,6 +26,7 @@ #include "nouveau_drv.h" #include "nouveau_connector.h" #include "nouveau_encoder.h" +#include "nouveau_crtc.h" static bool hdmi_sor(struct drm_encoder *encoder) @@ -36,12 +37,42 @@ hdmi_sor(struct drm_encoder *encoder) return true; } +static inline u32 +hdmi_base(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + if (!hdmi_sor(encoder)) + return 0x616500 + (nv_crtc->index * 0x800); + return 0x61c500 + (nv_encoder->or * 0x800); +} + +static void +hdmi_wr32(struct drm_encoder *encoder, u32 reg, u32 val) +{ + nv_wr32(encoder->dev, hdmi_base(encoder) + reg, val); +} + +static u32 +hdmi_rd32(struct drm_encoder *encoder, u32 reg) +{ + return nv_rd32(encoder->dev, hdmi_base(encoder) + reg); +} + +static u32 +hdmi_mask(struct drm_encoder *encoder, u32 reg, u32 mask, u32 val) +{ + u32 tmp = hdmi_rd32(encoder, reg); + hdmi_wr32(encoder, reg, (tmp & ~mask) | val); + return tmp; +} + static void nouveau_audio_disconnect(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_device *dev = encoder->dev; - int or = nv_encoder->or * 0x800; + u32 or = nv_encoder->or * 0x800; if (hdmi_sor(encoder)) { nv_mask(dev, 0x61c448 + or, 0x00000003, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index 2a638aeb5061..da603b1d8e3f 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -195,6 +195,7 @@ 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); + nv_encoder->crtc = encoder->crtc; switch (nv_encoder->dcb->type) { case OUTPUT_TMDS: @@ -206,7 +207,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, } else mode_ctl = 0x0200; - nouveau_hdmi_mode_set(encoder, mode); + nouveau_hdmi_mode_set(encoder, adjusted_mode); break; case OUTPUT_DP: nv_connector = nouveau_encoder_connector_get(nv_encoder); @@ -243,12 +244,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, ret = RING_SPACE(evo, 2); if (ret) { NV_ERROR(dev, "no space while connecting SOR\n"); + nv_encoder->crtc = NULL; return; } BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); OUT_RING(evo, mode_ctl); - - nv_encoder->crtc = encoder->crtc; } static struct drm_crtc * -- cgit v1.2.3 From b2337f2333c0bdefc9b230da17ed7188e4eb7f6c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 5 Oct 2011 14:59:14 +1000 Subject: drm/nouveau/hdmi: enable sending of avi/audio infoframes Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_hdmi.c | 103 +++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c index d8540a64a8d3..3b4120f0626a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c +++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c @@ -110,10 +110,107 @@ nouveau_audio_mode_set(struct drm_encoder *encoder, } } +static void +nouveau_hdmi_infoframe(struct drm_encoder *encoder, u32 ctrl, u8 *frame) +{ + /* calculate checksum for the infoframe */ + u8 sum = 0, i; + for (i = 0; i < frame[2]; i++) + sum += frame[i]; + frame[3] = 256 - sum; + + /* disable infoframe, and write header */ + hdmi_mask(encoder, ctrl + 0x00, 0x00000001, 0x00000000); + hdmi_wr32(encoder, ctrl + 0x08, *(u32 *)frame & 0xffffff); + + /* register scans tell me the audio infoframe has only one set of + * subpack regs, according to tegra (gee nvidia, it'd be nice if we + * could get those docs too!), the hdmi block pads out the rest of + * the packet on its own. + */ + if (ctrl == 0x020) + frame[2] = 6; + + /* write out checksum and data, weird weird 7 byte register pairs */ + for (i = 0; i < frame[2] + 1; i += 7) { + u32 rsubpack = ctrl + 0x0c + ((i / 7) * 8); + u32 *subpack = (u32 *)&frame[3 + i]; + hdmi_wr32(encoder, rsubpack + 0, subpack[0]); + hdmi_wr32(encoder, rsubpack + 4, subpack[1] & 0xffffff); + } + + /* enable the infoframe */ + hdmi_mask(encoder, ctrl, 0x00000001, 0x00000001); +} + +static void +nouveau_hdmi_video_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + const u8 Y = 0, A = 0, B = 0, S = 0, C = 0, M = 0, R = 0; + const u8 ITC = 0, EC = 0, Q = 0, SC = 0, VIC = 0, PR = 0; + const u8 bar_top = 0, bar_bottom = 0, bar_left = 0, bar_right = 0; + u8 frame[20]; + + frame[0x00] = 0x82; /* AVI infoframe */ + frame[0x01] = 0x02; /* version */ + frame[0x02] = 0x0d; /* length */ + frame[0x03] = 0x00; + frame[0x04] = (Y << 5) | (A << 4) | (B << 2) | S; + frame[0x05] = (C << 6) | (M << 4) | R; + frame[0x06] = (ITC << 7) | (EC << 4) | (Q << 2) | SC; + frame[0x07] = VIC; + frame[0x08] = PR; + frame[0x09] = bar_top & 0xff; + frame[0x0a] = bar_top >> 8; + frame[0x0b] = bar_bottom & 0xff; + frame[0x0c] = bar_bottom >> 8; + frame[0x0d] = bar_left & 0xff; + frame[0x0e] = bar_left >> 8; + frame[0x0f] = bar_right & 0xff; + frame[0x10] = bar_right >> 8; + frame[0x11] = 0x00; + frame[0x12] = 0x00; + frame[0x13] = 0x00; + + nouveau_hdmi_infoframe(encoder, 0x020, frame); +} + +static void +nouveau_hdmi_audio_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + const u8 CT = 0x00, CC = 0x01, ceaSS = 0x00, SF = 0x00, FMT = 0x00; + const u8 CA = 0x00, DM_INH = 0, LSV = 0x00; + u8 frame[12]; + + frame[0x00] = 0x84; /* Audio infoframe */ + frame[0x01] = 0x01; /* version */ + frame[0x02] = 0x0a; /* length */ + frame[0x03] = 0x00; + frame[0x04] = (CT << 4) | CC; + frame[0x05] = (SF << 2) | ceaSS; + frame[0x06] = FMT; + frame[0x07] = CA; + frame[0x08] = (DM_INH << 7) | (LSV << 3); + frame[0x09] = 0x00; + frame[0x0a] = 0x00; + frame[0x0b] = 0x00; + + nouveau_hdmi_infoframe(encoder, 0x000, frame); +} + static void nouveau_hdmi_disconnect(struct drm_encoder *encoder) { nouveau_audio_disconnect(encoder); + + /* disable audio and avi infoframes */ + hdmi_mask(encoder, 0x000, 0x00000001, 0x00000000); + hdmi_mask(encoder, 0x020, 0x00000001, 0x00000000); + + /* disable hdmi */ + hdmi_mask(encoder, 0x0a4, 0x40000000, 0x00000000); } void @@ -130,5 +227,11 @@ nouveau_hdmi_mode_set(struct drm_encoder *encoder, return; } + /* enable hdmi */ + hdmi_mask(encoder, 0x0a4, 0x40000000, 0x40000000); + + nouveau_hdmi_video_infoframe(encoder, mode); + nouveau_hdmi_audio_infoframe(encoder, mode); + nouveau_audio_mode_set(encoder, mode); } -- cgit v1.2.3 From 549cd872b0777bd72a66daa56558af28ec20d8a5 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 6 Oct 2011 11:51:45 +1000 Subject: drm/nv50/crtc: disable flip overlay around scaling mode changes Prevents EVO getting all angry at us. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_crtc.c | 79 +++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 39 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 882080e0b4f5..8eb108b28eb1 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -38,6 +38,39 @@ #include "nouveau_connector.h" #include "nv50_display.h" +static int +nv50_crtc_wait_complete(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + struct nv50_display *disp = nv50_display(dev); + struct nouveau_channel *evo = disp->master; + u64 start; + int ret; + + ret = RING_SPACE(evo, 6); + if (ret) + return ret; + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x80000000); + BEGIN_RING(evo, 0, 0x0080, 1); + OUT_RING (evo, 0); + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x00000000); + + nv_wo32(disp->ntfy, 0x000, 0x00000000); + FIRE_RING (evo); + + start = ptimer->read(dev); + do { + if (nv_ro32(disp->ntfy, 0x000)) + return 0; + } while (ptimer->read(dev) - start < 2000000000ULL); + + return -EBUSY; +} + static void nv50_crtc_lut_load(struct drm_crtc *crtc) { @@ -184,10 +217,11 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) { struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc); - struct drm_device *dev = nv_crtc->base.dev; + struct drm_crtc *crtc = &nv_crtc->base; + struct drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; struct drm_display_mode *native_mode = NULL; - struct drm_display_mode *mode = &nv_crtc->base.mode; + struct drm_display_mode *mode = &crtc->mode; uint32_t outX, outY, horiz, vert; int ret; @@ -231,7 +265,7 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) break; } - ret = RING_SPACE(evo, update ? 7 : 5); + ret = RING_SPACE(evo, 5); if (ret) return ret; @@ -251,9 +285,9 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) OUT_RING(evo, outY << 16 | outX); if (update) { - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING(evo, 0); - FIRE_RING(evo); + nv50_display_flip_stop(crtc); + nv50_crtc_wait_complete(crtc); + nv50_display_flip_next(crtc, crtc->fb, NULL); } return 0; @@ -441,39 +475,6 @@ nv50_crtc_dpms(struct drm_crtc *crtc, int mode) { } -static int -nv50_crtc_wait_complete(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; - struct nv50_display *disp = nv50_display(dev); - struct nouveau_channel *evo = disp->master; - u64 start; - int ret; - - ret = RING_SPACE(evo, 6); - if (ret) - return ret; - BEGIN_RING(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x80000000); - BEGIN_RING(evo, 0, 0x0080, 1); - OUT_RING (evo, 0); - BEGIN_RING(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x00000000); - - nv_wo32(disp->ntfy, 0x000, 0x00000000); - FIRE_RING (evo); - - start = ptimer->read(dev); - do { - if (nv_ro32(disp->ntfy, 0x000)) - return 0; - } while (ptimer->read(dev) - start < 2000000000ULL); - - return -EBUSY; -} - static void nv50_crtc_prepare(struct drm_crtc *crtc) { -- cgit v1.2.3 From 27d5030a235d89842ed70e18d924f017b34a496d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 6 Oct 2011 12:46:40 +1000 Subject: drm/nouveau: move master modesetting init to nouveau_display Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 47 ++++++++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 ++ drivers/gpu/drm/nouveau/nouveau_fb.h | 2 -- drivers/gpu/drm/nouveau/nouveau_state.c | 27 ++---------------- 4 files changed, 51 insertions(+), 27 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 7e88cd7f2b99..017d4ea12b1e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -147,11 +147,56 @@ nouveau_user_framebuffer_create(struct drm_device *dev, return &nouveau_fb->base; } -const struct drm_mode_config_funcs nouveau_mode_config_funcs = { +static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { .fb_create = nouveau_user_framebuffer_create, .output_poll_changed = nouveau_fbcon_output_poll_changed, }; +int +nouveau_display_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + int ret; + + 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 = disp->create(dev); + if (ret) + return ret; + + return 0; +} + +void +nouveau_display_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + + disp->destroy(dev); + drm_mode_config_cleanup(dev); +} + int nouveau_vblank_enable(struct drm_device *dev, int crtc) { diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index c99490d6ee7c..a55bae1e1e50 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1442,6 +1442,8 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); /* nouveau_display.c */ +int nouveau_display_create(struct drm_device *dev); +void nouveau_display_destroy(struct drm_device *dev); int nouveau_vblank_enable(struct drm_device *dev, int crtc); void nouveau_vblank_disable(struct drm_device *dev, int crtc); int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h index f4dd30150879..f3fb649fe454 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fb.h +++ b/drivers/gpu/drm/nouveau/nouveau_fb.h @@ -42,8 +42,6 @@ nouveau_framebuffer(struct drm_framebuffer *fb) return container_of(fb, struct nouveau_framebuffer, base); } -extern const struct drm_mode_config_funcs nouveau_mode_config_funcs; - int nouveau_framebuffer_init(struct drm_device *dev, struct nouveau_framebuffer *nouveau_fb, struct drm_mode_fb_cmd2 *mode_cmd, struct nouveau_bo *nvbo); #endif /* __NOUVEAU_FB_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 3d5cf6bb1c74..f3ee58283af0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -733,27 +733,7 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_fifo; - /* 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); + ret = nouveau_display_create(dev); if (ret) goto out_irq; @@ -789,7 +769,7 @@ out_fence: nouveau_fence_fini(dev); out_disp: nouveau_backlight_exit(dev); - engine->display.destroy(dev); + nouveau_display_destroy(dev); out_irq: nouveau_irq_fini(dev); out_fifo: @@ -850,8 +830,7 @@ static void nouveau_card_takedown(struct drm_device *dev) } nouveau_backlight_exit(dev); - engine->display.destroy(dev); - drm_mode_config_cleanup(dev); + nouveau_display_destroy(dev); if (!dev_priv->noaccel) { engine->fifo.takedown(dev); -- cgit v1.2.3 From b29caa5885e85bbda7c84ea55721b9e79718583a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 6 Oct 2011 13:29:05 +1000 Subject: drm/nouveau: add overscan compensation connector properties Exposes the same connector properties as the Radeon implementation, however their behaviour isn't exactly the same. The primary difference being that unless both hborder/vborder have been defined by the user, the driver will keep the aspect ratio of the overscanned area the same as the mode the display is programmed for. Enabled for digital outputs on GeForce 8 and up, excluding GF119. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 78 ++++++++++++++++++++--- drivers/gpu/drm/nouveau/nouveau_connector.h | 5 +- drivers/gpu/drm/nouveau/nouveau_display.c | 35 +++++++++- drivers/gpu/drm/nouveau/nouveau_drv.h | 10 +++ drivers/gpu/drm/nouveau/nv50_crtc.c | 99 +++++++++++++++++------------ 5 files changed, 176 insertions(+), 51 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index cea6696b1906..5e830a585538 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -420,15 +420,21 @@ static int nouveau_connector_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { + struct drm_nouveau_private *dev_priv = connector->dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); struct drm_device *dev = connector->dev; + struct nouveau_crtc *nv_crtc; int ret; + nv_crtc = NULL; + if (connector->encoder && connector->encoder->crtc) + nv_crtc = nouveau_crtc(connector->encoder->crtc); + /* Scaling mode */ if (property == dev->mode_config.scaling_mode_property) { - struct nouveau_crtc *nv_crtc = NULL; bool modeset = false; switch (value) { @@ -454,8 +460,6 @@ nouveau_connector_set_property(struct drm_connector *connector, modeset = true; nv_connector->scaling_mode = value; - if (connector->encoder && connector->encoder->crtc) - nv_crtc = nouveau_crtc(connector->encoder->crtc); if (!nv_crtc) return 0; @@ -475,18 +479,56 @@ nouveau_connector_set_property(struct drm_connector *connector, return 0; } + /* Underscan */ + if (property == disp->underscan_property) { + if (nv_connector->underscan != value) { + nv_connector->underscan = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, + nv_connector->scaling_mode, + true); + } + + return 0; + } + + if (property == disp->underscan_hborder_property) { + if (nv_connector->underscan_hborder != value) { + nv_connector->underscan_hborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, + nv_connector->scaling_mode, + true); + } + + return 0; + } + + if (property == disp->underscan_vborder_property) { + if (nv_connector->underscan_vborder != value) { + nv_connector->underscan_vborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, + nv_connector->scaling_mode, + true); + } + + return 0; + } + /* Dithering */ if (property == dev->mode_config.dithering_mode_property) { - struct nouveau_crtc *nv_crtc = NULL; - if (value == DRM_MODE_DITHERING_ON) nv_connector->use_dithering = true; else nv_connector->use_dithering = false; - if (connector->encoder && connector->encoder->crtc) - nv_crtc = nouveau_crtc(connector->encoder->crtc); - if (!nv_crtc || !nv_crtc->set_dither) return 0; @@ -773,6 +815,7 @@ nouveau_connector_create(struct drm_device *dev, int index) { const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_connector *nv_connector = NULL; struct dcb_connector_table_entry *dcb = NULL; @@ -857,6 +900,25 @@ nouveau_connector_create(struct drm_device *dev, int index) drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); } + /* Add overscan compensation options to digital outputs */ + if ((dev_priv->card_type == NV_50 || + dev_priv->card_type == NV_C0) && + (dcb->type == DCB_CONNECTOR_DVI_D || + dcb->type == DCB_CONNECTOR_DVI_I || + dcb->type == DCB_CONNECTOR_HDMI_0 || + dcb->type == DCB_CONNECTOR_HDMI_1 || + dcb->type == DCB_CONNECTOR_DP)) { + drm_connector_attach_property(connector, + disp->underscan_property, + UNDERSCAN_OFF); + drm_connector_attach_property(connector, + disp->underscan_hborder_property, + 0); + drm_connector_attach_property(connector, + disp->underscan_vborder_property, + 0); + } + switch (dcb->type) { case DCB_CONNECTOR_VGA: if (dev_priv->card_type >= NV_50) { diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 711b1e9203af..5bcaabbd375c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -35,8 +35,11 @@ struct nouveau_connector { struct dcb_connector_table_entry *dcb; - int scaling_mode; bool use_dithering; + int scaling_mode; + enum nouveau_underscan_type underscan; + u32 underscan_hborder; + u32 underscan_vborder; struct nouveau_encoder *detected_encoder; struct edid *edid; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 017d4ea12b1e..9bc9d6463b4a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -152,17 +152,50 @@ static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { .output_poll_changed = nouveau_fbcon_output_poll_changed, }; + +struct drm_prop_enum_list { + int type; + char *name; +}; + +static struct drm_prop_enum_list nouveau_underscan_enum_list[] = { + { UNDERSCAN_OFF, "off" }, + { UNDERSCAN_ON, "on" }, + { UNDERSCAN_AUTO, "auto" }, +}; + int nouveau_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_display_engine *disp = &dev_priv->engine.display; - int ret; + int ret, cnt, i; drm_mode_config_init(dev); drm_mode_create_scaling_mode_property(dev); drm_mode_create_dithering_property(dev); + cnt = ARRAY_SIZE(nouveau_underscan_enum_list); + disp->underscan_property = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "underscan", cnt); + for (i = 0; i < cnt; i++) { + drm_property_add_enum(disp->underscan_property, i, + nouveau_underscan_enum_list[i].type, + nouveau_underscan_enum_list[i].name); + } + + disp->underscan_hborder_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "underscan hborder", 2); + disp->underscan_hborder_property->values[0] = 0; + disp->underscan_hborder_property->values[1] = 128; + + disp->underscan_vborder_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "underscan vborder", 2); + disp->underscan_vborder_property->values[0] = 0; + disp->underscan_vborder_property->values[1] = 128; + dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index a55bae1e1e50..b4a5eba75998 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -391,6 +391,12 @@ struct nouveau_fifo_engine { void (*tlb_flush)(struct drm_device *dev); }; +enum nouveau_underscan_type { + UNDERSCAN_OFF, + UNDERSCAN_ON, + UNDERSCAN_AUTO, +}; + struct nouveau_display_engine { void *priv; int (*early_init)(struct drm_device *); @@ -398,6 +404,10 @@ struct nouveau_display_engine { int (*create)(struct drm_device *); int (*init)(struct drm_device *); void (*destroy)(struct drm_device *); + + struct drm_property *underscan_property; + struct drm_property *underscan_hborder_property; + struct drm_property *underscan_vborder_property; }; struct nouveau_gpio_engine { diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 8eb108b28eb1..e426a9b9865a 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -215,74 +215,91 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) static int nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) { - struct nouveau_connector *nv_connector = - nouveau_crtc_connector_get(nv_crtc); + struct nouveau_connector *nv_connector; struct drm_crtc *crtc = &nv_crtc->base; struct drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_display_mode *native_mode = NULL; struct drm_display_mode *mode = &crtc->mode; - uint32_t outX, outY, horiz, vert; + u32 ctrl = 0, oX, oY; int ret; NV_DEBUG_KMS(dev, "\n"); - switch (scaling_mode) { - case DRM_MODE_SCALE_NONE: - break; - default: - if (!nv_connector || !nv_connector->native_mode) { - NV_ERROR(dev, "No native mode, forcing panel scaling\n"); - scaling_mode = DRM_MODE_SCALE_NONE; + nv_connector = nouveau_crtc_connector_get(nv_crtc); + if (!nv_connector || !nv_connector->native_mode) { + NV_ERROR(dev, "no native mode, forcing panel scaling\n"); + scaling_mode = DRM_MODE_SCALE_NONE; + } + + /* start off at the resolution we programmed the crtc for, this + * effectively handles NONE/FULL scaling + */ + if (scaling_mode != DRM_MODE_SCALE_NONE) { + oX = nv_connector->native_mode->hdisplay; + oY = nv_connector->native_mode->vdisplay; + } else { + oX = mode->hdisplay; + oY = mode->vdisplay; + } + + /* add overscan compensation if necessary, will keep the aspect + * ratio the same as the backend mode unless overridden by the + * user setting both hborder and vborder properties. + */ + if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || + (nv_connector->underscan == UNDERSCAN_AUTO && + nv_connector->edid && + drm_detect_hdmi_monitor(nv_connector->edid)))) { + u32 bX = nv_connector->underscan_hborder; + u32 bY = nv_connector->underscan_vborder; + u32 aspect = (oY << 19) / oX; + + if (bX) { + oX -= (bX * 2); + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; } else { - native_mode = nv_connector->native_mode; + oX -= (oX >> 4) + 32; + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; } - break; } + /* handle CENTER/ASPECT scaling, taking into account the areas + * removed already for overscan compensation + */ switch (scaling_mode) { + case DRM_MODE_SCALE_CENTER: + oX = min((u32)mode->hdisplay, oX); + oY = min((u32)mode->vdisplay, oY); + /* fall-through */ case DRM_MODE_SCALE_ASPECT: - horiz = (native_mode->hdisplay << 19) / mode->hdisplay; - vert = (native_mode->vdisplay << 19) / mode->vdisplay; - - if (vert > horiz) { - outX = (mode->hdisplay * horiz) >> 19; - outY = (mode->vdisplay * horiz) >> 19; + if (oY < oX) { + u32 aspect = (mode->hdisplay << 19) / mode->vdisplay; + oX = ((oY * aspect) + (aspect / 2)) >> 19; } else { - outX = (mode->hdisplay * vert) >> 19; - outY = (mode->vdisplay * vert) >> 19; + u32 aspect = (mode->vdisplay << 19) / mode->hdisplay; + oY = ((oX * aspect) + (aspect / 2)) >> 19; } break; - case DRM_MODE_SCALE_FULLSCREEN: - outX = native_mode->hdisplay; - outY = native_mode->vdisplay; - break; - case DRM_MODE_SCALE_CENTER: - case DRM_MODE_SCALE_NONE: default: - outX = mode->hdisplay; - outY = mode->vdisplay; break; } + if (mode->hdisplay != oX || mode->vdisplay != oY || + mode->flags & DRM_MODE_FLAG_INTERLACE || + mode->flags & DRM_MODE_FLAG_DBLSCAN) + ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE; + ret = RING_SPACE(evo, 5); if (ret) return ret; - /* Got a better name for SCALER_ACTIVE? */ - /* One day i've got to really figure out why this is needed. */ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); - if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) || - (mode->flags & DRM_MODE_FLAG_INTERLACE) || - mode->hdisplay != outX || mode->vdisplay != outY) { - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE); - } else { - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE); - } - + OUT_RING (evo, ctrl); BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2); - OUT_RING(evo, outY << 16 | outX); - OUT_RING(evo, outY << 16 | outX); + OUT_RING (evo, oY << 16 | oX); + OUT_RING (evo, oY << 16 | oX); if (update) { nv50_display_flip_stop(crtc); -- cgit v1.2.3 From c983e6f660dd39758b032a2de4c8844ff2575278 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sun, 9 Oct 2011 22:58:32 +0200 Subject: drm/nv50: also report errors in MP1/MP2 when they happen. Signed-off-by: Maxim Levitsky Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_graph.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index ac601f7c4e1a..33d5711a918d 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -616,9 +616,9 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old, } break; case 7: /* MP error */ - if (ustatus & 0x00010000) { + if (ustatus & 0x04030000) { nv50_pgraph_mp_trap(dev, i, display); - ustatus &= ~0x00010000; + ustatus &= ~0x04030000; } break; case 8: /* TPDMA error */ -- cgit v1.2.3 From 4bfb94a1b40e694284df3ea7dfb66ce6252b6008 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sun, 9 Oct 2011 22:58:33 +0200 Subject: drm/nouveau: disable output polling through suspend. Because doing polling while hardware is disabled is a bad idea... Signed-off-by: Maxim Levitsky Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index f0a60afac446..982505b1be58 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -178,6 +178,8 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + NV_INFO(dev, "Disabling fbcon acceleration...\n"); nouveau_fbcon_save_disable_accel(dev); @@ -385,6 +387,7 @@ nouveau_pci_resume(struct pci_dev *pdev) drm_helper_resume_force_mode(dev); nouveau_fbcon_restore_accel(dev); + drm_kms_helper_poll_enable(dev); return 0; } -- cgit v1.2.3 From 71d91f655e549b0eeeb788decad7791ca6a0885b Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sun, 9 Oct 2011 22:58:35 +0200 Subject: drm/nouveau: restore performance mode a bit later. Otherwice code that responsible for idling the card can't work. BIOS init tables are supposed to init the clocks to correct values, so that shouldn't cause any problems (we don't reclock by default anyway) Signed-off-by: Maxim Levitsky Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 982505b1be58..1e1b4a47b700 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -298,8 +298,6 @@ nouveau_pci_resume(struct pci_dev *pdev) if (ret) return ret; - nouveau_pm_resume(dev); - if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) { ret = nouveau_mem_init_agp(dev); if (ret) { @@ -339,6 +337,8 @@ nouveau_pci_resume(struct pci_dev *pdev) } } + nouveau_pm_resume(dev); + NV_INFO(dev, "Restoring mode...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; -- cgit v1.2.3 From a4eaa0a0427ef13468d784953de40c3087e6d870 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sun, 9 Oct 2011 22:58:34 +0200 Subject: drm/nouveau: restore cursors after restoring mode PDISP doesn't like it when disabled CRTCs are poked. Fixes external output not coming to life when it has cursor on. https://bugs.freedesktop.org/show_bug.cgi?id=41608 Signed-off-by: Maxim Levitsky Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 1e1b4a47b700..c033a0a4f2b2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -362,15 +362,6 @@ nouveau_pci_resume(struct pci_dev *pdev) engine->display.init(dev); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 offset = nv_crtc->cursor.nvbo->bo.offset; - - nv_crtc->cursor.set_offset(nv_crtc, offset); - nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, - nv_crtc->cursor_saved_y); - } - /* Force CLUT to get re-loaded during modeset */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); @@ -386,6 +377,15 @@ nouveau_pci_resume(struct pci_dev *pdev) drm_helper_resume_force_mode(dev); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 offset = nv_crtc->cursor.nvbo->bo.offset; + + nv_crtc->cursor.set_offset(nv_crtc, offset); + nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, + nv_crtc->cursor_saved_y); + } + nouveau_fbcon_restore_accel(dev); drm_kms_helper_poll_enable(dev); return 0; -- cgit v1.2.3 From e6e039d10d08fbdecbdcaf20f29bb7a8c8580d4b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 14 Oct 2011 14:35:19 +1000 Subject: drm/nv50/disp: move sync routine to where it can be used by other modules Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_crtc.c | 41 ++++------------------------------ drivers/gpu/drm/nouveau/nv50_display.c | 32 ++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv50_display.h | 1 + 3 files changed, 37 insertions(+), 37 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index e426a9b9865a..f794b4e08f5f 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -38,39 +38,6 @@ #include "nouveau_connector.h" #include "nv50_display.h" -static int -nv50_crtc_wait_complete(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; - struct nv50_display *disp = nv50_display(dev); - struct nouveau_channel *evo = disp->master; - u64 start; - int ret; - - ret = RING_SPACE(evo, 6); - if (ret) - return ret; - BEGIN_RING(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x80000000); - BEGIN_RING(evo, 0, 0x0080, 1); - OUT_RING (evo, 0); - BEGIN_RING(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x00000000); - - nv_wo32(disp->ntfy, 0x000, 0x00000000); - FIRE_RING (evo); - - start = ptimer->read(dev); - do { - if (nv_ro32(disp->ntfy, 0x000)) - return 0; - } while (ptimer->read(dev) - start < 2000000000ULL); - - return -EBUSY; -} - static void nv50_crtc_lut_load(struct drm_crtc *crtc) { @@ -303,7 +270,7 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) if (update) { nv50_display_flip_stop(crtc); - nv50_crtc_wait_complete(crtc); + nv50_display_sync(dev); nv50_display_flip_next(crtc, crtc->fb, NULL); } @@ -515,7 +482,7 @@ nv50_crtc_commit(struct drm_crtc *crtc) nv50_crtc_blank(nv_crtc, false); drm_vblank_post_modeset(dev, nv_crtc->index); - nv50_crtc_wait_complete(crtc); + nv50_display_sync(dev); nv50_display_flip_next(crtc, crtc->fb, NULL); } @@ -710,7 +677,7 @@ nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, if (ret) return ret; - ret = nv50_crtc_wait_complete(crtc); + ret = nv50_display_sync(crtc->dev); if (ret) return ret; @@ -729,7 +696,7 @@ nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc, if (ret) return ret; - return nv50_crtc_wait_complete(crtc); + return nv50_display_sync(crtc->dev); } static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = { diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 06de250fe617..80b220f4b125 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -61,6 +61,38 @@ nv50_display_late_takedown(struct drm_device *dev) { } +int +nv50_display_sync(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + struct nv50_display *disp = nv50_display(dev); + struct nouveau_channel *evo = disp->master; + u64 start; + int ret; + + ret = RING_SPACE(evo, 6); + if (ret == 0) { + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x80000000); + BEGIN_RING(evo, 0, 0x0080, 1); + OUT_RING (evo, 0); + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x00000000); + + nv_wo32(disp->ntfy, 0x000, 0x00000000); + FIRE_RING (evo); + + start = ptimer->read(dev); + do { + if (nv_ro32(disp->ntfy, 0x000)) + return 0; + } while (ptimer->read(dev) - start < 2000000000ULL); + } + + return -EBUSY; +} + int nv50_display_init(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index c2da503a22aa..0cd8819a53f6 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -73,6 +73,7 @@ void nv50_display_destroy(struct drm_device *dev); int nv50_crtc_blank(struct nouveau_crtc *, bool blank); int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); +int nv50_display_sync(struct drm_device *); int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, struct nouveau_channel *chan); void nv50_display_flip_stop(struct drm_crtc *); -- cgit v1.2.3 From b98e3f5c9e42f70c171f67331773a575441eab3b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 14 Oct 2011 16:13:10 +1000 Subject: drm/nv50/disp: synchronise display right after init This has the effect of ensuring the encoders which were active before we loaded get disconnected properly before we start reprogramming them. Also removing a bit of cargo-cult from the initial evo pushbuf. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_crtc.c | 7 +++++-- drivers/gpu/drm/nouveau/nv50_display.c | 29 ++++++----------------------- 2 files changed, 11 insertions(+), 25 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index f794b4e08f5f..c1c57cb37208 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -627,7 +627,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, } } - ret = RING_SPACE(evo, 17); + ret = RING_SPACE(evo, 19); if (ret) return ret; @@ -652,7 +652,10 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, } BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1); - OUT_RING(evo, 0); + OUT_RING (evo, 0); + /* required to make display sync channel not hate life */ + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK900), 1); + OUT_RING (evo, 0x00000311); /* This is the actual resolution of the mode. */ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1); diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 80b220f4b125..a37e32e00ec8 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -210,31 +210,14 @@ nv50_display_init(struct drm_device *dev) nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9); - ret = RING_SPACE(evo, 15); + ret = RING_SPACE(evo, 3); if (ret) return ret; BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2); - OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED); - OUT_RING(evo, NvEvoSync); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1); - OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1); - OUT_RING(evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1); - OUT_RING(evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1); - OUT_RING(evo, 0); - /* required to make display sync channels not hate life */ - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK900), 1); - OUT_RING (evo, 0x00000311); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(1, UNK900), 1); - OUT_RING (evo, 0x00000311); - FIRE_RING(evo); - if (!nv_wait(dev, 0x640004, 0xffffffff, evo->dma.put << 2)) - NV_ERROR(dev, "evo pushbuf stalled\n"); - + OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED); + OUT_RING (evo, NvEvoSync); - return 0; + return nv50_display_sync(dev); } static int nv50_display_disable(struct drm_device *dev) @@ -754,8 +737,8 @@ nv50_display_unk20_handler(struct drm_device *dev) if (crtc >= 0) { pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)); pclk &= 0x003fffff; - - nv50_crtc_set_clock(dev, crtc, pclk); + if (pclk) + nv50_crtc_set_clock(dev, crtc, pclk); tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc)); tmp &= ~0x000000f; -- cgit v1.2.3 From 0f6ea564dec79271878b03884a978062bf86eb2a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 14 Oct 2011 16:19:42 +1000 Subject: drm/nv50/disp: completely reset disp if master evo channel active at init Should fix issues with kexec, and as a nice side bonus, the code to avoid having PDISP disappear will also fix hibernate on those effected systems. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_display.c | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index a37e32e00ec8..cfd7a8278470 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -50,9 +50,50 @@ nv50_sor_nr(struct drm_device *dev) return 4; } +static int +evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) +{ + int ret = 0; + if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) + NV_INFO(dev, "EvoPIO: %d 0x%04x 0x%08x\n", ch, mthd, data); + nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000001); + nv_wr32(dev, 0x610304 + (ch * 0x08), data); + nv_wr32(dev, 0x610300 + (ch * 0x08), 0x80000001 | mthd); + if (!nv_wait(dev, 0x610300 + (ch * 0x08), 0x80000000, 0x00000000)) + ret = -EBUSY; + nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000000); + return ret; +} + int nv50_display_early_init(struct drm_device *dev) { + int i; + /* check if master evo channel is already active, a good a sign as any + * that the display engine is in a weird state (hibernate/kexec), if + * it is, do our best to reset the display engine... + */ + if (nv_rd32(dev, 0x610200) & 0x00000001) { + NV_INFO(dev, "PDISP: already active, attempting to reset...\n"); + + /* deactivate both heads first, PDISP will disappear forever + * (well, until you power cycle) on some boards as soon as + * PMC_ENABLE is hit unless they are.. + */ + for (i = 0; i < 2; i++) { + evo_icmd(dev, 0, 0x0880 + (i * 0x400), 0x05000000); + evo_icmd(dev, 0, 0x089c + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0840 + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0844 + (i * 0x400), 0); + evo_icmd(dev, 0, 0x085c + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0874 + (i * 0x400), 0); + } + evo_icmd(dev, 0, 0x0080, 0); + + /* reset PDISP */ + nv_mask(dev, 0x000200, 0x40000000, 0x00000000); + nv_mask(dev, 0x000200, 0x40000000, 0x40000000); + } return 0; } -- cgit v1.2.3 From 7ae494e80c8bbad71a968a816d06cf83da4e9b93 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 14 Oct 2011 14:43:20 +1000 Subject: drm/nv50/disp: disconnect encoders before reprogramming them Fixes a case where we don't get separate supervisor interrupt sequences for disconnect and modeset events. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_dac.c | 7 +------ drivers/gpu/drm/nouveau/nv50_sor.c | 1 + 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c index 808f3ec8f827..a0f2bebf49e3 100644 --- a/drivers/gpu/drm/nouveau/nv50_dac.c +++ b/drivers/gpu/drm/nouveau/nv50_dac.c @@ -199,11 +199,6 @@ nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, return true; } -static void -nv50_dac_prepare(struct drm_encoder *encoder) -{ -} - static void nv50_dac_commit(struct drm_encoder *encoder) { @@ -266,7 +261,7 @@ static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = { .save = nv50_dac_save, .restore = nv50_dac_restore, .mode_fixup = nv50_dac_mode_fixup, - .prepare = nv50_dac_prepare, + .prepare = nv50_dac_disconnect, .commit = nv50_dac_commit, .mode_set = nv50_dac_mode_set, .get_crtc = nv50_dac_crtc_get, diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index da603b1d8e3f..3fab98a2f959 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -174,6 +174,7 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, static void nv50_sor_prepare(struct drm_encoder *encoder) { + nv50_sor_disconnect(encoder); } static void -- cgit v1.2.3 From 9976f15c27d573479d1cc79c81c2c2fc7ea93eb2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 17 Oct 2011 08:56:09 +1000 Subject: drm/nv50/disp: wait for encoder disconnect to complete before link training Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_sor.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index 3fab98a2f959..48c09f5cd4fb 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -174,7 +174,12 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, static void nv50_sor_prepare(struct drm_encoder *encoder) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); nv50_sor_disconnect(encoder); + if (nv_encoder->dcb->type == OUTPUT_DP) { + /* avoid race between link training and supervisor intr */ + nv50_display_sync(encoder->dev); + } } static void -- cgit v1.2.3 From 6322175530c89ab719cea28202f96a3660491727 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 17 Oct 2011 10:24:49 +1000 Subject: drm/nouveau: determine a value for display_info.bpc if edid doesn't Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 5e830a585538..fc42ea8503cb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -644,6 +644,46 @@ nouveau_connector_scaler_modes_add(struct drm_connector *connector) return modes; } +static void +nouveau_connector_detect_depth(struct drm_connector *connector) +{ + struct drm_nouveau_private *dev_priv = connector->dev->dev_private; + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct nvbios *bios = &dev_priv->vbios; + struct drm_display_mode *mode = nv_connector->native_mode; + bool duallink; + + /* if the edid is feeling nice enough to provide this info, use it */ + if (nv_connector->edid && connector->display_info.bpc) + return; + + /* if not, we're out of options unless we're LVDS, default to 6bpc */ + connector->display_info.bpc = 6; + if (nv_encoder->dcb->type != OUTPUT_LVDS) + return; + + /* LVDS: panel straps */ + if (bios->fp_no_ddc) { + if (bios->fp.if_is_24bit) + connector->display_info.bpc = 8; + return; + } + + /* LVDS: DDC panel, need to first determine the number of links to + * know which if_is_24bit flag to check... + */ + if (nv_connector->edid && + nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) + duallink = ((u8 *)nv_connector->edid)[121] == 2; + else + duallink = mode->clock >= bios->fp.duallink_transition_clk; + + if ((!duallink && (bios->fp.strapless_is_24bit & 1)) || + ( duallink && (bios->fp.strapless_is_24bit & 2))) + connector->display_info.bpc = 8; +} + static int nouveau_connector_get_modes(struct drm_connector *connector) { @@ -688,6 +728,12 @@ nouveau_connector_get_modes(struct drm_connector *connector) ret = 1; } + /* Attempt to determine display colour depth, this has to happen after + * we've determined the "native" mode for LVDS, as the VBIOS tables + * require us to compare against a pixel clock in some cases.. + */ + nouveau_connector_detect_depth(connector); + if (nv_encoder->dcb->type == OUTPUT_TV) ret = get_slave_funcs(encoder)->get_modes(encoder, connector); -- cgit v1.2.3 From 488ff207f98650c6a1f077e432b541f3cdcb7ab8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 17 Oct 2011 10:38:10 +1000 Subject: drm/nouveau: no need to pass parameters into set_scale/dither Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 17 +++++------------ drivers/gpu/drm/nouveau/nouveau_crtc.h | 4 ++-- drivers/gpu/drm/nouveau/nv50_crtc.c | 16 ++++++++++------ drivers/gpu/drm/nouveau/nvd0_display.c | 17 +++++++++-------- 4 files changed, 26 insertions(+), 28 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index fc42ea8503cb..372955ddc591 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -471,7 +471,7 @@ nouveau_connector_set_property(struct drm_connector *connector, if (!ret) return -EINVAL; } else { - ret = nv_crtc->set_scale(nv_crtc, value, true); + ret = nv_crtc->set_scale(nv_crtc, true); if (ret) return ret; } @@ -486,9 +486,7 @@ nouveau_connector_set_property(struct drm_connector *connector, if (!nv_crtc || !nv_crtc->set_scale) return 0; - return nv_crtc->set_scale(nv_crtc, - nv_connector->scaling_mode, - true); + return nv_crtc->set_scale(nv_crtc, true); } return 0; @@ -500,9 +498,7 @@ nouveau_connector_set_property(struct drm_connector *connector, if (!nv_crtc || !nv_crtc->set_scale) return 0; - return nv_crtc->set_scale(nv_crtc, - nv_connector->scaling_mode, - true); + return nv_crtc->set_scale(nv_crtc, true); } return 0; @@ -514,9 +510,7 @@ nouveau_connector_set_property(struct drm_connector *connector, if (!nv_crtc || !nv_crtc->set_scale) return 0; - return nv_crtc->set_scale(nv_crtc, - nv_connector->scaling_mode, - true); + return nv_crtc->set_scale(nv_crtc, true); } return 0; @@ -532,8 +526,7 @@ nouveau_connector_set_property(struct drm_connector *connector, if (!nv_crtc || !nv_crtc->set_dither) return 0; - return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, - true); + return nv_crtc->set_dither(nv_crtc, true); } if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV) diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index bf8e1289953d..b9b1980b5d8c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -67,8 +67,8 @@ struct nouveau_crtc { int depth; } lut; - int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update); - int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update); + int (*set_dither)(struct nouveau_crtc *crtc, bool update); + int (*set_scale)(struct nouveau_crtc *crtc, bool update); }; static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index c1c57cb37208..70d7fbb43cf6 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -132,10 +132,12 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) } static int -nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) +nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) { struct drm_device *dev = nv_crtc->base.dev; struct nouveau_channel *evo = nv50_display(dev)->master; + struct nouveau_connector *nv_connector = + nouveau_crtc_connector_get(nv_crtc); int ret; NV_DEBUG_KMS(dev, "\n"); @@ -147,7 +149,7 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) } BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1); - if (on) + if (nv_connector->use_dithering) OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON); else OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF); @@ -180,15 +182,15 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) } static int -nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) +nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) { struct nouveau_connector *nv_connector; struct drm_crtc *crtc = &nv_crtc->base; struct drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; struct drm_display_mode *mode = &crtc->mode; + int scaling_mode, ret; u32 ctrl = 0, oX, oY; - int ret; NV_DEBUG_KMS(dev, "\n"); @@ -196,6 +198,8 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) if (!nv_connector || !nv_connector->native_mode) { NV_ERROR(dev, "no native mode, forcing panel scaling\n"); scaling_mode = DRM_MODE_SCALE_NONE; + } else { + scaling_mode = nv_connector->scaling_mode; } /* start off at the resolution we programmed the crtc for, this @@ -663,8 +667,8 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1); OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0)); - nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false); - nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false); + nv_crtc->set_dither(nv_crtc, false); + nv_crtc->set_scale(nv_crtc, false); return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false); } diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index cb006a718e70..9e96eb12133a 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -107,13 +107,14 @@ nvd0_display_crtc_get(struct drm_encoder *encoder) * CRTC *****************************************************************************/ static int -nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) +nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) { + struct nouveau_connector *nv_connector; struct drm_device *dev = nv_crtc->base.dev; - u32 *push, mode; + u32 *push, mode = 0; - mode = 0x00000000; - if (on) { + nv_connector = nouveau_crtc_connector_get(nv_crtc); + if (nv_connector->use_dithering) { /* 0x11: 6bpc dynamic 2x2 * 0x13: 8bpc dynamic 2x2 * 0x19: 6bpc static 2x2 @@ -139,7 +140,7 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) } static int -nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update) +nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) { struct drm_display_mode *mode = &nv_crtc->base.mode; struct drm_device *dev = nv_crtc->base.dev; @@ -155,7 +156,7 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update) u32 xratio = (native->hdisplay << 19) / mode->hdisplay; u32 yratio = (native->vdisplay << 19) / mode->vdisplay; - switch (type) { + switch (nv_connector->scaling_mode) { case DRM_MODE_SCALE_ASPECT: if (xratio > yratio) { outX = (mode->hdisplay * yratio) >> 19; @@ -378,8 +379,8 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, } 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_dither(nv_crtc, false); + nvd0_crtc_set_scale(nv_crtc, false); nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, false); return 0; } -- cgit v1.2.3 From de69185573586302ada2e59ba41835df36986277 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 17 Oct 2011 12:23:41 +1000 Subject: drm/nouveau: improve dithering properties, and implement proper auto mode Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 37 +++++++++------ drivers/gpu/drm/nouveau/nouveau_connector.h | 27 ++++++++++- drivers/gpu/drm/nouveau/nouveau_display.c | 74 +++++++++++++++++++++++------ drivers/gpu/drm/nouveau/nouveau_drv.h | 8 +--- drivers/gpu/drm/nouveau/nv04_dfp.c | 5 +- drivers/gpu/drm/nouveau/nv50_crtc.c | 49 +++++++++++-------- drivers/gpu/drm/nouveau/nvd0_display.c | 27 ++++++----- 7 files changed, 158 insertions(+), 69 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 372955ddc591..a8da98fe1282 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -517,12 +517,16 @@ nouveau_connector_set_property(struct drm_connector *connector, } /* Dithering */ - if (property == dev->mode_config.dithering_mode_property) { - if (value == DRM_MODE_DITHERING_ON) - nv_connector->use_dithering = true; - else - nv_connector->use_dithering = false; + if (property == disp->dithering_mode) { + nv_connector->dithering_mode = value; + if (!nv_crtc || !nv_crtc->set_dither) + return 0; + + return nv_crtc->set_dither(nv_crtc, true); + } + if (property == disp->dithering_depth) { + nv_connector->dithering_depth = value; if (!nv_crtc || !nv_crtc->set_dither) return 0; @@ -918,7 +922,7 @@ nouveau_connector_create(struct drm_device *dev, int index) drm_connector_init(dev, connector, funcs, type); drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); - /* Check if we need dithering enabled */ + /* parse lvds table now, we depend on bios->fp.* values later */ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { bool dummy, is_24bit = false; @@ -928,8 +932,6 @@ nouveau_connector_create(struct drm_device *dev, int index) "LVDS\n"); goto fail; } - - nv_connector->use_dithering = !is_24bit; } /* Init DVI-I specific properties */ @@ -940,8 +942,7 @@ nouveau_connector_create(struct drm_device *dev, int index) } /* Add overscan compensation options to digital outputs */ - if ((dev_priv->card_type == NV_50 || - dev_priv->card_type == NV_C0) && + if (disp->underscan_property && (dcb->type == DCB_CONNECTOR_DVI_D || dcb->type == DCB_CONNECTOR_DVI_I || dcb->type == DCB_CONNECTOR_HDMI_0 || @@ -977,10 +978,18 @@ nouveau_connector_create(struct drm_device *dev, int index) drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, nv_connector->scaling_mode); - drm_connector_attach_property(connector, - dev->mode_config.dithering_mode_property, - nv_connector->use_dithering ? - DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); + if (disp->dithering_mode) { + nv_connector->dithering_mode = DITHERING_MODE_AUTO; + drm_connector_attach_property(connector, + disp->dithering_mode, + nv_connector->dithering_mode); + } + if (disp->dithering_depth) { + nv_connector->dithering_depth = DITHERING_DEPTH_AUTO; + drm_connector_attach_property(connector, + disp->dithering_depth, + nv_connector->dithering_depth); + } break; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 5bcaabbd375c..6468f5e67ccf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -30,12 +30,37 @@ #include "drm_edid.h" #include "nouveau_i2c.h" +enum nouveau_underscan_type { + UNDERSCAN_OFF, + UNDERSCAN_ON, + UNDERSCAN_AUTO, +}; + +/* the enum values specifically defined here match nv50/nvd0 hw values, and + * the code relies on this + */ +enum nouveau_dithering_mode { + DITHERING_MODE_OFF = 0x00, + DITHERING_MODE_ON = 0x01, + DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON, + DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON, + DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON, + DITHERING_MODE_AUTO +}; + +enum nouveau_dithering_depth { + DITHERING_DEPTH_6BPC = 0x00, + DITHERING_DEPTH_8BPC = 0x02, + DITHERING_DEPTH_AUTO +}; + struct nouveau_connector { struct drm_connector base; struct dcb_connector_table_entry *dcb; - bool use_dithering; + int dithering_mode; + int dithering_depth; int scaling_mode; enum nouveau_underscan_type underscan; u32 underscan_hborder; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 9bc9d6463b4a..bfc0090ddcf1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -32,6 +32,7 @@ #include "nouveau_hw.h" #include "nouveau_crtc.h" #include "nouveau_dma.h" +#include "nouveau_connector.h" #include "nv50_display.h" static void @@ -154,35 +155,78 @@ static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { struct drm_prop_enum_list { + u8 gen_mask; int type; char *name; }; -static struct drm_prop_enum_list nouveau_underscan_enum_list[] = { - { UNDERSCAN_OFF, "off" }, - { UNDERSCAN_ON, "on" }, - { UNDERSCAN_AUTO, "auto" }, +static struct drm_prop_enum_list underscan[] = { + { 2, UNDERSCAN_AUTO, "auto" }, + { 2, UNDERSCAN_OFF, "off" }, + { 2, UNDERSCAN_ON, "on" }, + {} }; +static struct drm_prop_enum_list dither_mode[] = { + { 7, DITHERING_MODE_AUTO, "auto" }, + { 7, DITHERING_MODE_OFF, "off" }, + { 1, DITHERING_MODE_ON, "on" }, + { 6, DITHERING_MODE_STATIC2X2, "static 2x2" }, + { 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" }, + { 4, DITHERING_MODE_TEMPORAL, "temporal" }, + {} +}; + +static struct drm_prop_enum_list dither_depth[] = { + { 6, DITHERING_DEPTH_AUTO, "auto" }, + { 6, DITHERING_DEPTH_6BPC, "6 bpc" }, + { 6, DITHERING_DEPTH_8BPC, "8 bpc" }, + {} +}; + +#define PROP_ENUM(p,gen,n,list) do { \ + struct drm_prop_enum_list *l = (list); \ + int c = 0; \ + while (l->gen_mask) { \ + if (l->gen_mask & (1 << (gen))) \ + c++; \ + l++; \ + } \ + if (c) { \ + p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c); \ + l = (list); \ + c = 0; \ + while (p && l->gen_mask) { \ + if (l->gen_mask & (1 << (gen))) { \ + drm_property_add_enum(p, c, l->type, l->name); \ + c++; \ + } \ + l++; \ + } \ + } \ +} while(0) + int nouveau_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_display_engine *disp = &dev_priv->engine.display; - int ret, cnt, i; + int ret, gen; drm_mode_config_init(dev); drm_mode_create_scaling_mode_property(dev); - drm_mode_create_dithering_property(dev); - - cnt = ARRAY_SIZE(nouveau_underscan_enum_list); - disp->underscan_property = drm_property_create(dev, DRM_MODE_PROP_ENUM, - "underscan", cnt); - for (i = 0; i < cnt; i++) { - drm_property_add_enum(disp->underscan_property, i, - nouveau_underscan_enum_list[i].type, - nouveau_underscan_enum_list[i].name); - } + + if (dev_priv->card_type < NV_50) + gen = 0; + else + if (dev_priv->card_type < NV_D0) + gen = 1; + else + gen = 2; + + PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode); + PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth); + PROP_ENUM(disp->underscan_property, gen, "underscan", underscan); disp->underscan_hborder_property = drm_property_create(dev, DRM_MODE_PROP_RANGE, diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index b4a5eba75998..156aea75d3e7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -391,12 +391,6 @@ struct nouveau_fifo_engine { void (*tlb_flush)(struct drm_device *dev); }; -enum nouveau_underscan_type { - UNDERSCAN_OFF, - UNDERSCAN_ON, - UNDERSCAN_AUTO, -}; - struct nouveau_display_engine { void *priv; int (*early_init)(struct drm_device *); @@ -405,6 +399,8 @@ struct nouveau_display_engine { int (*init)(struct drm_device *); void (*destroy)(struct drm_device *); + struct drm_property *dithering_mode; + struct drm_property *dithering_depth; struct drm_property *underscan_property; struct drm_property *underscan_hborder_property; struct drm_property *underscan_vborder_property; diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index 12098bf839c4..dff3ad594e0e 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -289,6 +289,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_display_mode *output_mode = &nv_encoder->mode; + struct drm_connector *connector = &nv_connector->base; uint32_t mode_ratio, panel_ratio; NV_DEBUG_KMS(dev, "Output mode on CRTC %d:\n", nv_crtc->index); @@ -407,7 +408,9 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, } /* Output property. */ - if (nv_connector->use_dithering) { + if ((nv_connector->dithering_mode == DITHERING_MODE_ON) || + (nv_connector->dithering_mode == DITHERING_MODE_AUTO && + encoder->crtc->fb->depth > connector->display_info.bpc * 3)) { if (dev_priv->chipset == 0x11) regp->dither = savep->dither | 0x00010000; else { diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 70d7fbb43cf6..dc30dc1a63b2 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -134,33 +134,40 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) static int nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) { - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_channel *evo = nv50_display(dev)->master; - struct nouveau_connector *nv_connector = - nouveau_crtc_connector_get(nv_crtc); - int ret; - - NV_DEBUG_KMS(dev, "\n"); + struct nouveau_channel *evo = nv50_display(nv_crtc->base.dev)->master; + struct nouveau_connector *nv_connector; + struct drm_connector *connector; + int head = nv_crtc->index, ret; + u32 mode = 0x00; - ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); - if (ret) { - NV_ERROR(dev, "no space while setting dither\n"); - return ret; + nv_connector = nouveau_crtc_connector_get(nv_crtc); + connector = &nv_connector->base; + if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { + if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = nv_connector->dithering_mode; } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1); - if (nv_connector->use_dithering) - OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON); - else - OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF); + if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { + if (connector->display_info.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= nv_connector->dithering_depth; + } - if (update) { - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING(evo, 0); - FIRE_RING(evo); + ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); + if (ret == 0) { + BEGIN_RING(evo, 0, NV50_EVO_CRTC(head, DITHER_CTRL), 1); + OUT_RING (evo, mode); + if (update) { + BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + OUT_RING (evo, 0); + FIRE_RING (evo); + } } - return 0; + return ret; } struct nouveau_connector * diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 9e96eb12133a..e9da42c73371 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -109,20 +109,25 @@ nvd0_display_crtc_get(struct drm_encoder *encoder) static int nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) { - struct nouveau_connector *nv_connector; struct drm_device *dev = nv_crtc->base.dev; - u32 *push, mode = 0; + struct nouveau_connector *nv_connector; + struct drm_connector *connector; + u32 *push, mode = 0x00; nv_connector = nouveau_crtc_connector_get(nv_crtc); - if (nv_connector->use_dithering) { - /* 0x11: 6bpc dynamic 2x2 - * 0x13: 8bpc dynamic 2x2 - * 0x19: 6bpc static 2x2 - * 0x1b: 8bpc static 2x2 - * 0x21: 6bpc temporal - * 0x23: 8bpc temporal - */ - mode = 0x00000011; + connector = &nv_connector->base; + if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { + if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = nv_connector->dithering_mode; + } + + if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { + if (connector->display_info.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= nv_connector->dithering_depth; } push = evo_wait(dev, 0, 4); -- cgit v1.2.3 From 4ceca5f8649125645fc4e7f178a1dca1345ab9b4 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 14 Oct 2011 23:55:47 +1000 Subject: drm/nouveau: don't pretend to support the DVI-I 'select subconnector' prop Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 5 +---- drivers/gpu/drm/nouveau/nouveau_display.c | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index a8da98fe1282..592fcfb9b389 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -935,11 +935,8 @@ nouveau_connector_create(struct drm_device *dev, int index) } /* Init DVI-I specific properties */ - if (dcb->type == DCB_CONNECTOR_DVI_I) { - drm_mode_create_dvi_i_properties(dev); + if (dcb->type == DCB_CONNECTOR_DVI_I) drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0); - drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); - } /* Add overscan compensation options to digital outputs */ if (disp->underscan_property && diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index bfc0090ddcf1..38623a25d0b1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -215,6 +215,7 @@ nouveau_display_create(struct drm_device *dev) drm_mode_config_init(dev); drm_mode_create_scaling_mode_property(dev); + drm_mode_create_dvi_i_properties(dev); if (dev_priv->card_type < NV_50) gen = 0; -- cgit v1.2.3 From c833442306c6578236af32561c229c9e7ca58079 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 19 Oct 2011 13:06:48 +1000 Subject: drm/nv50/disp: allow interlaced and doublescan modes on digital outputs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 592fcfb9b389..576fb9af5d13 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -198,6 +198,10 @@ nouveau_connector_set_encoder(struct drm_connector *connector, return; nv_connector->detected_encoder = nv_encoder; + if (dev_priv->card_type >= NV_50) { + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + } else if (nv_encoder->dcb->type == OUTPUT_LVDS || nv_encoder->dcb->type == OUTPUT_TMDS) { connector->doublescan_allowed = false; -- cgit v1.2.3 From a03a8623ad0b3b956524b7805303b5457acb1fb2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 20 Oct 2011 14:25:53 +1000 Subject: drm/nouveau/disp: kill off nouveau_crtc.mode This hasn't been necessary for a long time now.. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_crtc.h | 2 -- drivers/gpu/drm/nouveau/nv50_crtc.c | 10 ---------- drivers/gpu/drm/nouveau/nv50_sor.c | 16 ++++++++-------- 3 files changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index b9b1980b5d8c..686f6b4a1da3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -32,8 +32,6 @@ struct nouveau_crtc { int index; - struct drm_display_mode *mode; - uint32_t dpms_saved_fp_control; uint32_t fp_users; int saturation; diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index dc30dc1a63b2..bbbaa758d835 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -362,7 +362,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc) nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - kfree(nv_crtc->mode); kfree(nv_crtc); } @@ -604,8 +603,6 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, /* Find the connector attached to this CRTC */ nv_connector = nouveau_crtc_connector_get(nv_crtc); - *nv_crtc->mode = *adjusted_mode; - NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start; @@ -736,12 +733,6 @@ nv50_crtc_create(struct drm_device *dev, int index) if (!nv_crtc) return -ENOMEM; - nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL); - if (!nv_crtc->mode) { - kfree(nv_crtc); - return -ENOMEM; - } - /* Default CLUT parameters, will be activated on the hw upon * first mode set. */ @@ -763,7 +754,6 @@ nv50_crtc_create(struct drm_device *dev, int index) } if (ret) { - kfree(nv_crtc->mode); kfree(nv_crtc); return ret; } diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index 48c09f5cd4fb..c4423ba9c9bf 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -188,8 +188,8 @@ nv50_sor_commit(struct drm_encoder *encoder) } static void -nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, + struct drm_display_mode *mode) { struct nouveau_channel *evo = nv50_display(encoder->dev)->master; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); @@ -206,22 +206,22 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, switch (nv_encoder->dcb->type) { case OUTPUT_TMDS: if (nv_encoder->dcb->sorconf.link & 1) { - if (adjusted_mode->clock < 165000) + if (mode->clock < 165000) mode_ctl = 0x0100; else mode_ctl = 0x0500; } else mode_ctl = 0x0200; - nouveau_hdmi_mode_set(encoder, adjusted_mode); + nouveau_hdmi_mode_set(encoder, mode); break; case OUTPUT_DP: nv_connector = nouveau_encoder_connector_get(nv_encoder); if (nv_connector && nv_connector->base.display_info.bpc == 6) { - nv_encoder->dp.datarate = crtc->mode->clock * 18 / 8; + nv_encoder->dp.datarate = mode->clock * 18 / 8; mode_ctl |= 0x00020000; } else { - nv_encoder->dp.datarate = crtc->mode->clock * 24 / 8; + nv_encoder->dp.datarate = mode->clock * 24 / 8; mode_ctl |= 0x00050000; } @@ -239,10 +239,10 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, else mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) + if (mode->flags & DRM_MODE_FLAG_NHSYNC) mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) + if (mode->flags & DRM_MODE_FLAG_NVSYNC) mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); -- cgit v1.2.3 From 616a5f57b634718e87c260820166b11bdcf6a482 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 20 Oct 2011 15:00:22 +1000 Subject: drm/nv50/disp: rewrite crtc timing calculation, with proper names and fixes Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_crtc.c | 127 ++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 71 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index bbbaa758d835..998ac7195419 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -588,89 +588,74 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, } static int -nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, +nv50_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 drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nouveau_connector *nv_connector = NULL; - uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end; - uint32_t hunk1, vunk1, vunk2a, vunk2b; + u32 head = nv_crtc->index * 0x400; + u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; + u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; + u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; + u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; + u32 vblan2e = 0, vblan2s = 1; int ret; - /* Find the connector attached to this CRTC */ - nv_connector = nouveau_crtc_connector_get(nv_crtc); - - NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + /* hw timing description looks like this: + * + * <---------display---------> + * ______ + * |____________|---------------------------|____________| + * + * ^ synce ^ blanke ^ blanks ^ active + * + * interlaced modes also have 2 additional values pointing at the end + * and start of the next field's blanking period. + */ - hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start; - vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start; - hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start; - vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start; - /* I can't give this a proper name, anyone else can? */ - hunk1 = adjusted_mode->htotal - - adjusted_mode->hsync_start + adjusted_mode->hdisplay; - vunk1 = adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vdisplay; - /* Another strange value, this time only for interlaced adjusted_modes. */ - vunk2a = 2 * adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vdisplay; - vunk2b = adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vtotal; - - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { - vsync_dur /= 2; - vsync_start_to_end /= 2; - vunk1 /= 2; - vunk2a /= 2; - vunk2b /= 2; - /* magic */ - if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) { - vsync_start_to_end -= 1; - vunk1 -= 1; - vunk2a -= 1; - vunk2b -= 1; - } + hactive = mode->htotal; + hsynce = mode->hsync_end - mode->hsync_start - 1; + hbackp = mode->htotal - mode->hsync_end; + hblanke = hsynce + hbackp; + hfrontp = mode->hsync_start - mode->hdisplay; + hblanks = mode->htotal - hfrontp - 1; + + vactive = mode->vtotal * vscan / ilace; + vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; + vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; + vblanke = vsynce + vbackp; + vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; + vblanks = vactive - vfrontp - 1; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vblan2e = vactive + vsynce + vbackp; + vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); + vactive = (vactive * 2) + 1; } - ret = RING_SPACE(evo, 19); - if (ret) - return ret; - - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2); - OUT_RING(evo, adjusted_mode->clock | 0x800000); - OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0); - - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5); - OUT_RING(evo, 0); - OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal); - OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1)); - OUT_RING(evo, (vsync_start_to_end - 1) << 16 | - (hsync_start_to_end - 1)); - OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1)); - - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1); - OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1)); - } else { - OUT_RING(evo, 0); - OUT_RING(evo, 0); + ret = RING_SPACE(evo, 18); + if (ret == 0) { + BEGIN_RING(evo, 0, 0x0804 + head, 2); + OUT_RING (evo, 0x00800000 | mode->clock); + OUT_RING (evo, (ilace == 2) ? 2 : 0); + BEGIN_RING(evo, 0, 0x0810 + head, 6); + OUT_RING (evo, 0x00000000); /* border colour */ + OUT_RING (evo, (vactive << 16) | hactive); + OUT_RING (evo, ( vsynce << 16) | hsynce); + OUT_RING (evo, (vblanke << 16) | hblanke); + OUT_RING (evo, (vblanks << 16) | hblanks); + OUT_RING (evo, (vblan2e << 16) | vblan2s); + BEGIN_RING(evo, 0, 0x082c + head, 1); + OUT_RING (evo, 0x00000000); + BEGIN_RING(evo, 0, 0x0900 + head, 1); + OUT_RING (evo, 0x00000311); /* makes sync channel work */ + BEGIN_RING(evo, 0, 0x08c8 + head, 1); + OUT_RING (evo, (umode->vdisplay << 16) | umode->hdisplay); + BEGIN_RING(evo, 0, 0x08d4 + head, 1); + OUT_RING (evo, 0x00000000); /* screen position */ } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1); - OUT_RING (evo, 0); - /* required to make display sync channel not hate life */ - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK900), 1); - OUT_RING (evo, 0x00000311); - - /* This is the actual resolution of the mode. */ - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1); - OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1); - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0)); - nv_crtc->set_dither(nv_crtc, false); nv_crtc->set_scale(nv_crtc, false); -- cgit v1.2.3 From 1cb9469ee7bcdc0f80a3b12a1036352039ada72f Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 25 Oct 2011 16:29:13 +1000 Subject: drm/nv50/disp: fix scaling of doublescan modes Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_crtc.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 998ac7195419..8f6c2ace3adf 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -195,7 +195,8 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) struct drm_crtc *crtc = &nv_crtc->base; struct drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_display_mode *mode = &crtc->mode; + struct drm_display_mode *umode = &crtc->mode; + struct drm_display_mode *omode; int scaling_mode, ret; u32 ctrl = 0, oX, oY; @@ -212,13 +213,15 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) /* start off at the resolution we programmed the crtc for, this * effectively handles NONE/FULL scaling */ - if (scaling_mode != DRM_MODE_SCALE_NONE) { - oX = nv_connector->native_mode->hdisplay; - oY = nv_connector->native_mode->vdisplay; - } else { - oX = mode->hdisplay; - oY = mode->vdisplay; - } + if (scaling_mode != DRM_MODE_SCALE_NONE) + omode = nv_connector->native_mode; + else + omode = umode; + + oX = omode->hdisplay; + oY = omode->vdisplay; + if (omode->flags & DRM_MODE_FLAG_DBLSCAN) + oY *= 2; /* add overscan compensation if necessary, will keep the aspect * ratio the same as the backend mode unless overridden by the @@ -248,15 +251,15 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) */ switch (scaling_mode) { case DRM_MODE_SCALE_CENTER: - oX = min((u32)mode->hdisplay, oX); - oY = min((u32)mode->vdisplay, oY); + oX = min((u32)umode->hdisplay, oX); + oY = min((u32)umode->vdisplay, oY); /* fall-through */ case DRM_MODE_SCALE_ASPECT: if (oY < oX) { - u32 aspect = (mode->hdisplay << 19) / mode->vdisplay; + u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; oX = ((oY * aspect) + (aspect / 2)) >> 19; } else { - u32 aspect = (mode->vdisplay << 19) / mode->hdisplay; + u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; oY = ((oX * aspect) + (aspect / 2)) >> 19; } break; @@ -264,9 +267,9 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) break; } - if (mode->hdisplay != oX || mode->vdisplay != oY || - mode->flags & DRM_MODE_FLAG_INTERLACE || - mode->flags & DRM_MODE_FLAG_DBLSCAN) + if (umode->hdisplay != oX || umode->vdisplay != oY || + umode->flags & DRM_MODE_FLAG_INTERLACE || + umode->flags & DRM_MODE_FLAG_DBLSCAN) ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE; ret = RING_SPACE(evo, 5); -- cgit v1.2.3 From 6109183794a711d80c08705d477d2a19b437d5c1 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 22 Oct 2011 01:40:40 +0200 Subject: drm/nvd0: read temperature as we did on nv84+ boards Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index f3ee58283af0..31bca1dca85d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -477,6 +477,7 @@ 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.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; -- cgit v1.2.3 From dd1da8de172057b36860f427777ecfa293bb8f6c Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sun, 10 Jul 2011 00:08:41 +0200 Subject: drm/nouveau/pm: make clocks_set return an error code clocks_set can fail. Reporting an error is better than silently refusing to reclock. V2: Use the same logic on nv40 Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 +- drivers/gpu/drm/nouveau/nouveau_pm.h | 4 ++-- drivers/gpu/drm/nouveau/nv40_pm.c | 7 +++++-- drivers/gpu/drm/nouveau/nva3_pm.c | 6 +++++- 4 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 156aea75d3e7..067bda411a21 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -555,7 +555,7 @@ struct nouveau_pm_engine { 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 (*clocks_set)(struct drm_device *, void *); int (*voltage_get)(struct drm_device *); int (*voltage_set)(struct drm_device *, int voltage); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 5c87afde82d6..41050feb5b90 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -55,7 +55,7 @@ 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 *); +int nv40_pm_clocks_set(struct drm_device *, void *); int nv40_pm_pwm_get(struct drm_device *, struct dcb_gpio_entry *, u32*, u32*); int nv40_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); @@ -70,7 +70,7 @@ int nv50_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); /* nva3_pm.c */ 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 *); +int nva3_pm_clocks_set(struct drm_device *, void *); /* nvc0_pm.c */ int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index 02d5be8bff1b..3d5a5a7856e6 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -222,7 +222,7 @@ nv40_pm_gr_idle(void *data) return true; } -void +int nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -231,7 +231,7 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) struct bit_entry M; u32 crtc_mask = 0; u8 sr1[2]; - int i; + int i, ret = -EAGAIN; /* determine which CRTCs are active, fetch VGA_SR1 for each */ for (i = 0; i < 2; i++) { @@ -263,6 +263,8 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) if (!nv_wait_cb(dev, nv40_pm_gr_idle, dev)) goto resume; + ret = 0; + /* set engine clocks */ nv_mask(dev, 0x00c040, 0x00000333, 0x00000000); nv_wr32(dev, 0x004004, info->npll_coef); @@ -345,6 +347,7 @@ resume: spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); kfree(info); + return ret; } int diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 618c144b7a30..9e636e6ef6d7 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -287,12 +287,13 @@ nva3_pm_grcp_idle(void *data) return false; } -void +int 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; + int ret = -EAGAIN; /* prevent any new grctx switches from starting */ spin_lock_irqsave(&dev_priv->context_switch_lock, flags); @@ -328,6 +329,8 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) nv_wr32(dev, 0x100210, 0x80000000); } + ret = 0; + cleanup: /* unfreeze PFIFO */ nv_mask(dev, 0x002504, 0x00000001, 0x00000000); @@ -339,4 +342,5 @@ cleanup: nv_mask(dev, 0x400824, 0x10000000, 0x10000000); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); kfree(info); + return ret; } -- cgit v1.2.3 From d4cca9e1fccb9f7804ddfbbc2aebff7be23faa1e Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Thu, 6 Oct 2011 23:47:58 +0200 Subject: drm/nv50/pm: s/PLL_UNK05/PLL_VDEC/ Following to "drm/nv50/pm: s/unk05/vdec/", let's rename the PLL to PLL_VDEC PLL names are purely indicative and are based on the most important engine it clocks. Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 2 +- drivers/gpu/drm/nouveau/nouveau_bios.h | 2 +- drivers/gpu/drm/nouveau/nouveau_pm.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 41899eccf570..89747bebbfc7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -4719,7 +4719,7 @@ static struct pll_mapping nv84_pll_mapping[] = { { PLL_CORE , 0x004028 }, { PLL_SHADER, 0x004020 }, { PLL_MEMORY, 0x004008 }, - { PLL_UNK05 , 0x004030 }, + { PLL_VDEC , 0x004030 }, { PLL_UNK41 , 0x00e818 }, { PLL_VPLL0 , 0x614100 }, { PLL_VPLL1 , 0x614900 }, diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index a1e31ea2ecbf..dff121d8896f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -196,7 +196,7 @@ enum pll_types { PLL_SHADER = 0x02, PLL_UNK03 = 0x03, PLL_MEMORY = 0x04, - PLL_UNK05 = 0x05, + PLL_VDEC = 0x05, PLL_UNK40 = 0x40, PLL_UNK41 = 0x41, PLL_UNK42 = 0x42, diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index ea620d8de007..9177358616b0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -154,7 +154,7 @@ 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->vdec); + nouveau_pm_clock_set(dev, perflvl, PLL_VDEC, perflvl->vdec); } pm->cur = perflvl; @@ -220,7 +220,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (ret > 0) perflvl->shader = ret; - ret = pm->clock_get(dev, PLL_UNK05); + ret = pm->clock_get(dev, PLL_VDEC); if (ret > 0) perflvl->vdec = ret; } -- cgit v1.2.3 From f3fbaf34e2b1459eab248c5f0180928e7861120b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 26 Oct 2011 09:11:02 +1000 Subject: drm/nv50/pm: rewrite clock management, and switch to the new pm hooks This area is horrifically complicated on these chipsets, and it's likely we will need at least a few more tweaks yet. Oh yes, and it's completely disabled on IGPs for the moment. From traces, things look potentially different there yet again. Sigh... Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_perf.c | 1 + drivers/gpu/drm/nouveau/nouveau_pm.h | 7 +- drivers/gpu/drm/nouveau/nouveau_state.c | 6 +- drivers/gpu/drm/nouveau/nv50_pm.c | 658 +++++++++++++++++++++++++++----- 5 files changed, 576 insertions(+), 97 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 067bda411a21..95892ce9b59c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -493,6 +493,7 @@ struct nouveau_pm_level { u32 copy; u32 daemon; u32 vdec; + u32 dom6; u32 unka0; /* nva3:nvc0 */ u32 hub01; /* nvc0- */ u32 hub06; /* nvc0- */ diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index da584e3a8f6a..6d49bdbf93d0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -302,6 +302,7 @@ nouveau_perf_init(struct drm_device *dev) perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; perflvl->vdec = ROM16(entry[16]) * 1000; + perflvl->dom6 = ROM16(entry[20]) * 1000; break; case 0x40: #define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000 diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 41050feb5b90..06df411ca5fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -60,10 +60,9 @@ int nv40_pm_pwm_get(struct drm_device *, struct dcb_gpio_entry *, u32*, u32*); int nv40_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); /* 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 *, - u32 id, int khz); -void nv50_pm_clock_set(struct drm_device *, void *); +int nv50_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nv50_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +int nv50_pm_clocks_set(struct drm_device *, void *); int nv50_pm_pwm_get(struct drm_device *, struct dcb_gpio_entry *, u32*, u32*); int nv50_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 31bca1dca85d..6b4aaec648b9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -356,9 +356,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) case 0xaa: case 0xac: case 0x50: - engine->pm.clock_get = nv50_pm_clock_get; - engine->pm.clock_pre = nv50_pm_clock_pre; - engine->pm.clock_set = nv50_pm_clock_set; + engine->pm.clocks_get = nv50_pm_clocks_get; + engine->pm.clocks_pre = nv50_pm_clocks_pre; + engine->pm.clocks_set = nv50_pm_clocks_set; break; default: engine->pm.clocks_get = nva3_pm_clocks_get; diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 8a56880e4e71..bf9fd95c81c0 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -25,123 +25,601 @@ #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_bios.h" +#include "nouveau_hw.h" #include "nouveau_pm.h" -struct nv50_pm_state { - struct nouveau_pm_level *perflvl; - struct pll_lims pll; - enum pll_types type; - int N, M, P; +enum clk_src { + clk_src_crystal, + clk_src_href, + clk_src_hclk, + clk_src_hclkm3, + clk_src_hclkm3d2, + clk_src_host, + clk_src_nvclk, + clk_src_sclk, + clk_src_mclk, + clk_src_vdec, + clk_src_dom6 }; +static u32 read_clk(struct drm_device *, enum clk_src); + +static u32 +read_div(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + switch (dev_priv->chipset) { + case 0x50: /* it exists, but only has bit 31, not the dividers.. */ + case 0x84: + case 0x86: + case 0x98: + case 0xa0: + return nv_rd32(dev, 0x004700); + case 0x92: + case 0x94: + case 0x96: + return nv_rd32(dev, 0x004800); + default: + return 0x00000000; + } +} + +static u32 +read_pll_ref(struct drm_device *dev, u32 base) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 coef, ref = read_clk(dev, clk_src_crystal); + u32 rsel = nv_rd32(dev, 0x00e18c); + int P, N, M, id; + + switch (dev_priv->chipset) { + case 0x50: + case 0xa0: + switch (base) { + case 0x4020: + case 0x4028: id = !!(rsel & 0x00000004); break; + case 0x4008: id = !!(rsel & 0x00000008); break; + case 0x4030: id = 0; break; + default: + NV_ERROR(dev, "ref: bad pll 0x%06x\n", base); + return 0; + } + + coef = nv_rd32(dev, 0x00e81c + (id * 0x0c)); + ref *= (coef & 0x01000000) ? 2 : 4; + P = (coef & 0x00070000) >> 16; + N = ((coef & 0x0000ff00) >> 8) + 1; + M = ((coef & 0x000000ff) >> 0) + 1; + break; + case 0x84: + case 0x86: + case 0x92: + coef = nv_rd32(dev, 0x00e81c); + P = (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + case 0x94: + case 0x96: + case 0x98: + rsel = nv_rd32(dev, 0x00c050); + switch (base) { + case 0x4020: rsel = (rsel & 0x00000003) >> 0; break; + case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break; + case 0x4028: rsel = (rsel & 0x00001800) >> 11; break; + case 0x4030: rsel = 3; break; + default: + NV_ERROR(dev, "ref: bad pll 0x%06x\n", base); + return 0; + } + + switch (rsel) { + case 0: id = 1; break; + case 1: return read_clk(dev, clk_src_crystal); + case 2: return read_clk(dev, clk_src_href); + case 3: id = 0; break; + } + + coef = nv_rd32(dev, 0x00e81c + (id * 0x28)); + P = (nv_rd32(dev, 0x00e824 + (id * 0x28)) >> 16) & 7; + P += (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + default: + BUG_ON(1); + } + + if (M) + return (ref * N / M) >> P; + return 0; +} + +static u32 +read_pll(struct drm_device *dev, u32 base) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mast = nv_rd32(dev, 0x00c040); + u32 src = 0, ref = 0, clk = 0; + u32 ctrl, coef; + int N1, N2, M1, M2; + + switch (base) { + case 0x004028: + if (mast & 0x00100000) { + /* wtf, appears to only disable post-divider on nva0 */ + if (dev_priv->chipset != 0xa0) + return read_clk(dev, clk_src_dom6); + } + src = !!(mast & 0x00200000); + break; + case 0x004020: + src = !!(mast & 0x00400000); + break; + case 0x004008: + src = !!(mast & 0x00010000); + break; + case 0x004030: + src = !!(mast & 0x02000000); + break; + case 0x00e810: + ref = read_clk(dev, clk_src_crystal); + break; + default: + NV_ERROR(dev, "bad pll 0x%06x\n", base); + return 0; + } + + if (ref == 0) { + if (src) + ref = read_clk(dev, clk_src_href); + else + ref = read_pll_ref(dev, base); + } + + ctrl = nv_rd32(dev, base + 0); + coef = nv_rd32(dev, base + 4); + + N2 = (coef & 0xff000000) >> 24; + M2 = (coef & 0x00ff0000) >> 16; + N1 = (coef & 0x0000ff00) >> 8; + M1 = (coef & 0x000000ff); + if ((ctrl & 0x80000000) && M1) { + clk = ref * N1 / M1; + if ((ctrl & 0x40000100) == 0x40000000) { + if (M2) + clk = clk * N2 / M2; + else + clk = 0; + } + } + + return clk; +} + +static u32 +read_clk(struct drm_device *dev, enum clk_src src) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mast = nv_rd32(dev, 0x00c040); + u32 P = 0; + + switch (src) { + case clk_src_crystal: + return dev_priv->crystal; + case clk_src_href: + return 100000; /* PCIE reference clock */ + case clk_src_hclk: + return read_clk(dev, clk_src_href) * 27778 / 10000; + case clk_src_hclkm3: + return read_clk(dev, clk_src_hclk) * 3; + case clk_src_hclkm3d2: + return read_clk(dev, clk_src_hclk) * 3 / 2; + case clk_src_host: + switch (mast & 0x30000000) { + case 0x00000000: return read_clk(dev, clk_src_href); + case 0x10000000: break; + case 0x20000000: /* !0x50 */ + case 0x30000000: return read_clk(dev, clk_src_hclk); + } + break; + case clk_src_nvclk: + if (!(mast & 0x00100000)) + P = (nv_rd32(dev, 0x004028) & 0x00070000) >> 16; + switch (mast & 0x00000003) { + case 0x00000000: return read_clk(dev, clk_src_crystal) >> P; + case 0x00000001: return read_clk(dev, clk_src_dom6); + case 0x00000002: return read_pll(dev, 0x004020) >> P; + case 0x00000003: return read_pll(dev, 0x004028) >> P; + } + break; + case clk_src_sclk: + P = (nv_rd32(dev, 0x004020) & 0x00070000) >> 16; + switch (mast & 0x00000030) { + case 0x00000000: + if (mast & 0x00000080) + return read_clk(dev, clk_src_host) >> P; + return read_clk(dev, clk_src_crystal) >> P; + case 0x00000010: break; + case 0x00000020: return read_pll(dev, 0x004028) >> P; + case 0x00000030: return read_pll(dev, 0x004020) >> P; + } + break; + case clk_src_mclk: + P = (nv_rd32(dev, 0x004008) & 0x00070000) >> 16; + if (nv_rd32(dev, 0x004008) & 0x00000200) { + switch (mast & 0x0000c000) { + case 0x00000000: + return read_clk(dev, clk_src_crystal) >> P; + case 0x00008000: + case 0x0000c000: + return read_clk(dev, clk_src_href) >> P; + } + } else { + return read_pll(dev, 0x004008) >> P; + } + break; + case clk_src_vdec: + P = (read_div(dev) & 0x00000700) >> 8; + switch (dev_priv->chipset) { + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0xa0: + switch (mast & 0x00000c00) { + case 0x00000000: + if (dev_priv->chipset == 0xa0) /* wtf?? */ + return read_clk(dev, clk_src_nvclk) >> P; + return read_clk(dev, clk_src_crystal) >> P; + case 0x00000400: + return 0; + case 0x00000800: + if (mast & 0x01000000) + return read_pll(dev, 0x004028) >> P; + return read_pll(dev, 0x004030) >> P; + case 0x00000c00: + return read_clk(dev, clk_src_nvclk) >> P; + } + break; + case 0x98: + switch (mast & 0x00000c00) { + case 0x00000000: + return read_clk(dev, clk_src_nvclk) >> P; + case 0x00000400: + return 0; + case 0x00000800: + return read_clk(dev, clk_src_hclkm3d2) >> P; + case 0x00000c00: + return read_pll(dev, clk_src_mclk) >> P; + } + break; + } + break; + case clk_src_dom6: + switch (dev_priv->chipset) { + case 0x50: + case 0xa0: + return read_pll(dev, 0x00e810) >> 2; + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + P = (read_div(dev) & 0x00000007) >> 0; + switch (mast & 0x0c000000) { + case 0x00000000: return read_clk(dev, clk_src_href); + case 0x04000000: break; + case 0x08000000: return read_clk(dev, clk_src_hclk); + case 0x0c000000: + return read_clk(dev, clk_src_hclkm3) >> P; + } + break; + default: + break; + } + default: + break; + } + + NV_DEBUG(dev, "unknown clock source %d 0x%08x\n", src, mast); + return 0; +} + int -nv50_pm_clock_get(struct drm_device *dev, u32 id) +nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct pll_lims pll; - int P, N, M, ret; - u32 reg0, reg1; + struct drm_nouveau_private *dev_priv = dev->dev_private; + if (dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac) + return 0; + + perflvl->core = read_clk(dev, clk_src_nvclk); + perflvl->shader = read_clk(dev, clk_src_sclk); + perflvl->memory = read_clk(dev, clk_src_mclk); + if (dev_priv->chipset != 0x50) { + perflvl->vdec = read_clk(dev, clk_src_vdec); + perflvl->dom6 = read_clk(dev, clk_src_dom6); + } - ret = get_pll_limits(dev, id, &pll); + return 0; +} + +struct nv50_pm_state { + u32 emast; + u32 nctrl; + u32 ncoef; + u32 sctrl; + u32 scoef; + + u32 amast; + u32 pdivs; + + u32 mscript; + u32 mctrl; + u32 mcoef; +}; + +static u32 +calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll, + u32 clk, int *N1, int *M1, int *log2P) +{ + struct nouveau_pll_vals coef; + int ret; + + ret = get_pll_limits(dev, reg, pll); if (ret) - return ret; + return 0; + + pll->vco2.maxfreq = 0; + pll->refclk = read_pll_ref(dev, reg); + if (!pll->refclk) + return 0; + + ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef); + if (ret == 0) + return 0; - reg0 = nv_rd32(dev, pll.reg + 0); - reg1 = nv_rd32(dev, pll.reg + 4); - - if ((reg0 & 0x80000000) == 0) { - if (id == PLL_SHADER) { - NV_DEBUG(dev, "Shader PLL is disabled. " - "Shader clock is twice the core\n"); - ret = nv50_pm_clock_get(dev, PLL_CORE); - if (ret > 0) - return ret << 1; - } else if (id == PLL_MEMORY) { - NV_DEBUG(dev, "Memory PLL is disabled. " - "Memory clock is equal to the ref_clk\n"); - return pll.refclk; + *N1 = coef.N1; + *M1 = coef.M1; + *log2P = coef.log2P; + return ret; +} + +static inline u32 +calc_div(u32 src, u32 target, int *div) +{ + u32 clk0 = src, clk1 = src; + for (*div = 0; *div <= 7; (*div)++) { + if (clk0 <= target) { + clk1 = clk0 << (*div ? 1 : 0); + break; } + clk0 >>= 1; } - P = (reg0 & 0x00070000) >> 16; - N = (reg1 & 0x0000ff00) >> 8; - M = (reg1 & 0x000000ff); + if (target - clk0 <= clk1 - target) + return clk0; + (*div)--; + return clk1; +} - return ((pll.refclk * N / M) >> P); +static inline u32 +clk_same(u32 a, u32 b) +{ + return ((a / 1000) == (b / 1000)); } void * -nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u32 id, int khz) +nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct nv50_pm_state *state; - int dummy, ret; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_pm_state *info; + struct pll_lims pll; + int ret = -EINVAL; + int N, M, P1, P2; + u32 clk, out; + + if (dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac) + return ERR_PTR(-ENODEV); - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return ERR_PTR(-ENOMEM); - state->type = id; - state->perflvl = perflvl; - ret = get_pll_limits(dev, id, &state->pll); - if (ret < 0) { - kfree(state); - return (ret == -ENOENT) ? NULL : ERR_PTR(ret); + /* core: for the moment at least, always use nvpll */ + clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); + if (clk == 0) + goto error; + + info->emast = 0x00000003; + info->nctrl = 0x80000000 | (P1 << 19) | (P1 << 16); + info->ncoef = (N << 8) | M; + + /* shader: tie to nvclk if possible, otherwise use spll. have to be + * very careful that the shader clock is at least twice the core, or + * some chipsets will be very unhappy. i expect most or all of these + * cases will be handled by tying to nvclk, but it's possible there's + * corners + */ + if (P1-- && perflvl->shader == (perflvl->core << 1)) { + info->emast |= 0x00000020; + info->sctrl = 0x00000000 | (P1 << 19) | (P1 << 16); + info->scoef = nv_rd32(dev, 0x004024); + } else { + clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1); + if (clk == 0) + goto error; + + info->emast |= 0x00000030; + info->sctrl = 0x80000000 | (P1 << 19) | (P1 << 16); + info->scoef = (N << 8) | M; } - ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, &state->M, - &dummy, &dummy, &state->P); - if (ret < 0) { - kfree(state); - return ERR_PTR(ret); + /* memory: use pcie refclock if possible, otherwise use mpll */ + info->mscript = perflvl->memscript; + if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) { + info->mctrl = nv_rd32(dev, 0x4008) | 0x00000200; + info->mcoef = nv_rd32(dev, 0x400c); + } else + if (perflvl->memory) { + clk = calc_pll(dev, 0x4008, &pll, perflvl->memory, + &N, &M, &P1); + if (clk == 0) + goto error; + + info->mctrl = 0x80000000 | (P1 << 22) | (P1 << 16); + info->mctrl |= pll.log2p_bias << 19; + info->mcoef = (N << 8) | M; + } else { + info->mctrl = 0x00000000; } - return state; + /* vdec: avoid modifying xpll until we know exactly how the other + * clock domains work, i suspect at least some of them can also be + * tied to xpll... + */ + info->amast = info->pdivs = 0; + if (perflvl->vdec) { + /* see how close we can get using nvclk as a source */ + clk = calc_div(perflvl->core, perflvl->vdec, &P1); + + /* see how close we can get using xpll/hclk as a source */ + if (dev_priv->chipset != 0x98) + out = read_pll(dev, 0x004030); + else + out = read_clk(dev, clk_src_hclkm3d2); + out = calc_div(out, perflvl->vdec, &P2); + + /* select whichever gets us closest */ + if (abs((int)perflvl->vdec - clk) <= + abs((int)perflvl->vdec - out)) { + if (dev_priv->chipset != 0x98) + info->amast |= 0x00000c00; + else + info->amast |= 0x00000000; + info->pdivs |= P1 << 8; + } else { + info->amast |= 0x00000800; + info->pdivs |= P2 << 8; + } + } + + /* dom6: nfi what this is, but we're limited to various combinations + * of the host clock frequency + */ + if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) { + info->amast |= 0x00000000; + info->pdivs |= read_div(dev) & 0x00000007; + } else + if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) { + info->amast |= 0x08000000; + info->pdivs |= read_div(dev) & 0x00000007; + } else + if (perflvl->dom6) { + clk = read_clk(dev, clk_src_hclk) * 3; + clk = calc_div(clk, perflvl->dom6, &P1); + + info->amast |= 0x0c000000; + info->pdivs |= P1; + } + + + return info; +error: + kfree(info); + return ERR_PTR(ret); } -void -nv50_pm_clock_set(struct drm_device *dev, void *pre_state) +int +nv50_pm_clocks_set(struct drm_device *dev, void *data) { - struct nv50_pm_state *state = pre_state; - struct nouveau_pm_level *perflvl = state->perflvl; - u32 reg = state->pll.reg, tmp; - struct bit_entry BIT_M; - u16 script; - int N = state->N; - int M = state->M; - int P = state->P; - - if (state->type == PLL_MEMORY && perflvl->memscript && - bit_table(dev, 'M', &BIT_M) == 0 && - BIT_M.version == 1 && BIT_M.length >= 0x0b) { - script = ROM16(BIT_M.data[0x05]); - if (script) - 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, -1); - script = ROM16(BIT_M.data[0x09]); - if (script) - nouveau_bios_run_init_table(dev, script, NULL, -1); - - nouveau_bios_run_init_table(dev, perflvl->memscript, NULL, -1); - } - - if (state->type == PLL_MEMORY) { - nv_wr32(dev, 0x100210, 0); - nv_wr32(dev, 0x1002dc, 1); - } - - tmp = nv_rd32(dev, reg + 0) & 0xfff8ffff; - tmp |= 0x80000000 | (P << 16); - nv_wr32(dev, reg + 0, tmp); - nv_wr32(dev, reg + 4, (N << 8) | M); - - if (state->type == PLL_MEMORY) { - nv_wr32(dev, 0x1002dc, 0); - nv_wr32(dev, 0x100210, 0x80000000); - } - - kfree(state); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_pm_state *info = data; + struct bit_entry M; + int ret = 0; + + /* halt and idle execution engines */ + nv_mask(dev, 0x002504, 0x00000001, 0x00000001); + if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) + goto error; + + /* reclock vdec/dom6 */ + nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000); + switch (dev_priv->chipset) { + case 0x92: + case 0x94: + case 0x96: + nv_mask(dev, 0x004800, 0x00000707, info->pdivs); + break; + default: + nv_mask(dev, 0x004700, 0x00000707, info->pdivs); + break; + } + nv_mask(dev, 0x00c040, 0x0c000c00, info->amast); + + /* core/shader: switch core to dom6, shader to hclk */ + if (dev_priv->chipset == 0x50) + nv_mask(dev, 0x00c040, 0x001000b0, 0x00100080); /* grrr! */ + else + nv_mask(dev, 0x00c040, 0x000000b3, 0x00000081); + nv_mask(dev, 0x004020, 0xc03f0100, info->sctrl); + nv_wr32(dev, 0x004024, info->scoef); + nv_mask(dev, 0x004028, 0xc03f0100, info->nctrl); + nv_wr32(dev, 0x00402c, info->ncoef); + nv_mask(dev, 0x00c040, 0x00100033, info->emast); + + /* memory */ + if (!info->mctrl) + goto resume; + + /* execute some scripts that do ??? from the vbios.. */ + if (!bit_table(dev, 'M', &M) && M.version == 1) { + if (M.length >= 6) + nouveau_bios_init_exec(dev, ROM16(M.data[5])); + if (M.length >= 8) + nouveau_bios_init_exec(dev, ROM16(M.data[7])); + if (M.length >= 10) + nouveau_bios_init_exec(dev, ROM16(M.data[9])); + nouveau_bios_init_exec(dev, info->mscript); + } + + /* disable display */ + nv_wr32(dev, 0x611200, 0x00003300); + udelay(100); + + /* 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 */ + + /* modify mpll */ + nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000); + nv_mask(dev, 0x004008, 0x81ff0200, 0x00000200 | info->mctrl); + nv_wr32(dev, 0x00400c, info->mcoef); + udelay(100); + nv_mask(dev, 0x004008, 0x81ff0200, info->mctrl); + + /* re-enable normal operation of memory controller */ + nv_wr32(dev, 0x1002dc, 0x00000000); + nv_mask(dev, 0x100210, 0x80000000, 0x80000000); + udelay(100); + + /* re-enable display */ + nv_wr32(dev, 0x611200, 0x00003330); + + goto resume; +error: + ret = -EBUSY; +resume: + nv_mask(dev, 0x002504, 0x00000001, 0x00000000); + return ret; } static int -- cgit v1.2.3 From 36f1317ed05f76bfafb442f70575cdaed6da2d8c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 27 Oct 2011 10:24:12 +1000 Subject: drm/nv04-nv30/pm: port to newer interfaces Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.h | 7 +- drivers/gpu/drm/nouveau/nouveau_state.c | 24 +++---- drivers/gpu/drm/nouveau/nv04_pm.c | 109 ++++++++++++++++++++++---------- 3 files changed, 91 insertions(+), 49 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 06df411ca5fe..663088d30428 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -47,10 +47,9 @@ void nouveau_mem_timing_init(struct drm_device *); void nouveau_mem_timing_fini(struct drm_device *); /* nv04_pm.c */ -int nv04_pm_clock_get(struct drm_device *, u32 id); -void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, - u32 id, int khz); -void nv04_pm_clock_set(struct drm_device *, void *); +int nv04_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nv04_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +int nv04_pm_clocks_set(struct drm_device *, void *); /* nv40_pm.c */ int nv40_pm_clocks_get(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 6b4aaec648b9..3e3798f7f369 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -87,9 +87,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->gpio.get = NULL; engine->gpio.set = NULL; 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 = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; @@ -136,9 +136,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 = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; @@ -185,9 +185,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 = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->vram.init = nouveau_mem_detect; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; @@ -234,9 +234,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 = nv04_pm_clocks_get; + engine->pm.clocks_pre = nv04_pm_clocks_pre; + engine->pm.clocks_set = nv04_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->vram.init = nouveau_mem_detect; diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c index 9ae92a87b8cc..6e7589918fa9 100644 --- a/drivers/gpu/drm/nouveau/nv04_pm.c +++ b/drivers/gpu/drm/nouveau/nv04_pm.c @@ -27,68 +27,111 @@ #include "nouveau_hw.h" #include "nouveau_pm.h" -struct nv04_pm_state { +int +nv04_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + int ret; + + ret = nouveau_hw_get_clock(dev, PLL_CORE); + if (ret < 0) + return ret; + perflvl->core = ret; + + ret = nouveau_hw_get_clock(dev, PLL_MEMORY); + if (ret < 0) + return ret; + perflvl->memory = ret; + + return 0; +} + +struct nv04_pm_clock { struct pll_lims pll; struct nouveau_pll_vals calc; }; -int -nv04_pm_clock_get(struct drm_device *dev, u32 id) +struct nv04_pm_state { + struct nv04_pm_clock core; + struct nv04_pm_clock memory; +}; + +static int +calc_pll(struct drm_device *dev, u32 id, int khz, struct nv04_pm_clock *clk) { - return nouveau_hw_get_clock(dev, id); + int ret; + + ret = get_pll_limits(dev, id, &clk->pll); + if (ret) + return ret; + + ret = nouveau_calc_pll_mnp(dev, &clk->pll, khz, &clk->calc); + if (!ret) + return -EINVAL; + + return 0; } void * -nv04_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u32 id, int khz) +nv04_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct nv04_pm_state *state; + struct nv04_pm_state *info; int ret; - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) return ERR_PTR(-ENOMEM); - ret = get_pll_limits(dev, id, &state->pll); - if (ret) { - kfree(state); - return (ret == -ENOENT) ? NULL : ERR_PTR(ret); - } + ret = calc_pll(dev, PLL_CORE, perflvl->core, &info->core); + if (ret) + goto error; - ret = nouveau_calc_pll_mnp(dev, &state->pll, khz, &state->calc); - if (!ret) { - kfree(state); - return ERR_PTR(-EINVAL); + if (perflvl->memory) { + ret = calc_pll(dev, PLL_MEMORY, perflvl->memory, &info->memory); + if (ret) + goto error; } - return state; + return info; +error: + kfree(info); + return ERR_PTR(ret); } -void -nv04_pm_clock_set(struct drm_device *dev, void *pre_state) +static void +prog_pll(struct drm_device *dev, struct nv04_pm_clock *clk) { 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; + u32 reg = clk->pll.reg; /* thank the insane nouveau_hw_setpll() interface for this */ if (dev_priv->card_type >= NV_40) reg += 4; - nouveau_hw_setpll(dev, reg, &state->calc); + nouveau_hw_setpll(dev, reg, &clk->calc); +} + +int +nv04_pm_clocks_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; + + prog_pll(dev, &state->core); - if (dev_priv->card_type < NV_30 && reg == NV_PRAMDAC_MPLL_COEFF) { - if (dev_priv->card_type == NV_20) - nv_mask(dev, 0x1002c4, 0, 1 << 20); + if (state->memory.pll.reg) { + prog_pll(dev, &state->memory); + if (dev_priv->card_type < NV_30) { + if (dev_priv->card_type == NV_20) + nv_mask(dev, 0x1002c4, 0, 1 << 20); - /* Reset the DLLs */ - nv_mask(dev, 0x1002c0, 0, 1 << 8); + /* Reset the DLLs */ + nv_mask(dev, 0x1002c0, 0, 1 << 8); + } } - if (reg == NV_PRAMDAC_NVPLL_COEFF) - ptimer->init(dev); + ptimer->init(dev); kfree(state); + return 0; } - -- cgit v1.2.3 From ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 27 Oct 2011 10:28:17 +1000 Subject: drm/nouveau/pm: remove the older interfaces completely Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 5 --- drivers/gpu/drm/nouveau/nouveau_pm.c | 62 +++++------------------------------ drivers/gpu/drm/nouveau/nv04_timer.c | 3 +- 3 files changed, 10 insertions(+), 60 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 95892ce9b59c..dc88248f2305 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -549,11 +549,6 @@ struct nouveau_pm_engine { struct device *hwmon; struct notifier_block acpi_nb; - int (*clock_get)(struct drm_device *, u32 id); - 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 *); int (*clocks_set)(struct drm_device *, void *); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 9177358616b0..4df8e0090df3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -96,31 +96,12 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) return -ENODEV; } -static int -nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u8 id, u32 khz) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - void *pre_state; - - if (khz == 0) - return 0; - - pre_state = pm->clock_pre(dev, perflvl, id, khz); - if (IS_ERR(pre_state)) - return PTR_ERR(pre_state); - - if (pre_state) - pm->clock_set(dev, pre_state); - return 0; -} - static int nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + void *state; int ret; if (perflvl == pm->cur) @@ -144,18 +125,10 @@ 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); - nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); - nouveau_pm_clock_set(dev, perflvl, PLL_VDEC, perflvl->vdec); - } + state = pm->clocks_pre(dev, perflvl); + if (IS_ERR(state)) + return PTR_ERR(state); + pm->clocks_set(dev, state); pm->cur = perflvl; return 0; @@ -202,28 +175,9 @@ 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) - perflvl->core = 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_VDEC); - if (ret > 0) - perflvl->vdec = ret; - } + ret = pm->clocks_get(dev, perflvl); + if (ret) + return ret; if (pm->voltage.supported && pm->voltage_get) { ret = pm->voltage_get(dev); diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c index 263301b809dd..55c945290e52 100644 --- a/drivers/gpu/drm/nouveau/nv04_timer.c +++ b/drivers/gpu/drm/nouveau/nv04_timer.c @@ -2,6 +2,7 @@ #include "drm.h" #include "nouveau_drv.h" #include "nouveau_drm.h" +#include "nouveau_hw.h" int nv04_timer_init(struct drm_device *dev) @@ -17,7 +18,7 @@ nv04_timer_init(struct drm_device *dev) /* determine base clock for timer source */ if (dev_priv->chipset < 0x40) { - n = dev_priv->engine.pm.clock_get(dev, PLL_CORE); + n = nouveau_hw_get_clock(dev, PLL_CORE); } else if (dev_priv->chipset == 0x40) { /*XXX: figure this out */ -- cgit v1.2.3 From 0b627a0b23404d97d1720c0c1abaee602aee9518 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 27 Oct 2011 12:02:12 +1000 Subject: drm/nouveau/pm: change volt/fan before upclock, but after downclock Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 49 ++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 4df8e0090df3..c6ebf693ffb8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -97,39 +97,62 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) } static int -nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) +nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl, + struct nouveau_pm_level *a, struct nouveau_pm_level *b) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - void *state; int ret; - if (perflvl == pm->cur) - return 0; - /*XXX: not on all boards, we should control based on temperature * on recent boards.. or maybe on some other factor we don't * know about? */ - if (perflvl->fanspeed) { + if (a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) { ret = nouveau_pwmfan_set(dev, perflvl->fanspeed); - if (ret && ret != -ENODEV) - NV_ERROR(dev, "set fanspeed failed: %d\n", ret); + if (ret && ret != -ENODEV) { + NV_ERROR(dev, "fanspeed set failed: %d\n", ret); + return ret; + } } - 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->volt_min, ret); + if (pm->voltage.supported && pm->voltage_set) { + if (a->volt_min && b->volt_min && b->volt_min > a->volt_min) { + ret = pm->voltage_set(dev, perflvl->volt_min); + if (ret) { + NV_ERROR(dev, "voltage set failed: %d\n", ret); + return ret; + } } } + return 0; +} + +static int +nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + void *state; + int ret; + + if (perflvl == pm->cur) + return 0; + + ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl); + if (ret) + return ret; + state = pm->clocks_pre(dev, perflvl); if (IS_ERR(state)) return PTR_ERR(state); pm->clocks_set(dev, state); + ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur); + if (ret) + return ret; + pm->cur = perflvl; return 0; } -- cgit v1.2.3 From 19fa224f8a91a73a39b35251ced04b59b2a171b4 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 28 Oct 2011 22:10:15 +1000 Subject: drm/nv50/pm: free state struct after setting clocks Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index bf9fd95c81c0..53b6c6c75744 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -619,6 +619,7 @@ error: ret = -EBUSY; resume: nv_mask(dev, 0x002504, 0x00000001, 0x00000000); + kfree(info); return ret; } -- cgit v1.2.3 From 020c6bf39488c4dd162bafed04b631b98f85d9f0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 29 Oct 2011 11:31:29 +1000 Subject: drm/nv50/disp: stricter check for evo being active on init Signed-off-by: Ben Skeggs Tested-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_display.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index cfd7a8278470..a59d0b923cc8 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -54,13 +54,13 @@ static int evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) { int ret = 0; - if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) - NV_INFO(dev, "EvoPIO: %d 0x%04x 0x%08x\n", ch, mthd, data); nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000001); nv_wr32(dev, 0x610304 + (ch * 0x08), data); nv_wr32(dev, 0x610300 + (ch * 0x08), 0x80000001 | mthd); if (!nv_wait(dev, 0x610300 + (ch * 0x08), 0x80000000, 0x00000000)) ret = -EBUSY; + if (ret || (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)) + NV_INFO(dev, "EvoPIO: %d 0x%04x 0x%08x\n", ch, mthd, data); nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000000); return ret; } @@ -68,13 +68,15 @@ evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) int nv50_display_early_init(struct drm_device *dev) { + u32 ctrl = nv_rd32(dev, 0x610200); int i; + /* check if master evo channel is already active, a good a sign as any * that the display engine is in a weird state (hibernate/kexec), if * it is, do our best to reset the display engine... */ - if (nv_rd32(dev, 0x610200) & 0x00000001) { - NV_INFO(dev, "PDISP: already active, attempting to reset...\n"); + if ((ctrl & 0x00000003) == 0x00000003) { + NV_INFO(dev, "PDISP: EVO(0) 0x%08x, resetting...\n", ctrl); /* deactivate both heads first, PDISP will disappear forever * (well, until you power cycle) on some boards as soon as @@ -94,6 +96,7 @@ nv50_display_early_init(struct drm_device *dev) nv_mask(dev, 0x000200, 0x40000000, 0x00000000); nv_mask(dev, 0x000200, 0x40000000, 0x40000000); } + return 0; } -- cgit v1.2.3 From be7f2615d7d14221a106e6c4ec3a64558e6190ed Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 28 Oct 2011 12:06:42 +1000 Subject: drm/nvc0/gr: update fuc source to assemble with latest envyas Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_graph.fuc | 56 +++---- drivers/gpu/drm/nouveau/nvc0_grgpc.fuc | 158 ++++++++++---------- drivers/gpu/drm/nouveau/nvc0_grhub.fuc | 266 ++++++++++++++++----------------- 3 files changed, 240 insertions(+), 240 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.fuc b/drivers/gpu/drm/nouveau/nvc0_graph.fuc index 2a4b6dc8f9de..e6b228844a32 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_graph.fuc @@ -71,9 +71,9 @@ queue_put: ld b32 $r9 D[$r13 + 0x4] // PUT xor $r8 8 cmpu b32 $r8 $r9 - bra ne queue_put_next + bra ne #queue_put_next mov $r15 E_CMD_OVERFLOW - call error + call #error ret // store cmd/data on queue @@ -104,7 +104,7 @@ queue_get: ld b32 $r8 D[$r13 + 0x0] // GET ld b32 $r9 D[$r13 + 0x4] // PUT cmpu b32 $r8 $r9 - bra e queue_get_done + bra e #queue_get_done // fetch first cmd/data pair and $r9 $r8 7 shl b32 $r9 3 @@ -135,9 +135,9 @@ nv_rd32: nv_rd32_wait: iord $r12 I[$r11 + 0x000] xbit $r12 $r12 31 - bra ne nv_rd32_wait + bra ne #nv_rd32_wait mov $r10 6 // DONE_MMIO_RD - call wait_doneo + call #wait_doneo iord $r15 I[$r11 + 0x100] // MMIO_RDVAL ret @@ -157,7 +157,7 @@ nv_wr32: nv_wr32_wait: iord $r12 I[$r11 + 0x000] xbit $r12 $r12 31 - bra ne nv_wr32_wait + bra ne #nv_wr32_wait ret // (re)set watchdog timer @@ -193,7 +193,7 @@ $1: shl b32 $r8 6 iord $r8 I[$r8 + 0x000] // DONE xbit $r8 $r8 $r10 - bra $2 wait_done_$1 + bra $2 #wait_done_$1 trace_clr(T_WAIT) ret ') @@ -216,7 +216,7 @@ mmctx_size: add b32 $r9 $r8 add b32 $r14 4 cmpu b32 $r14 $r15 - bra ne nv_mmctx_size_loop + bra ne #nv_mmctx_size_loop mov b32 $r15 $r9 ret @@ -238,12 +238,12 @@ mmctx_xfer: shl b32 $r8 6 clear b32 $r9 or $r11 $r11 - bra e mmctx_base_disabled + bra e #mmctx_base_disabled iowr I[$r8 + 0x000] $r11 // MMCTX_BASE bset $r9 0 // BASE_EN mmctx_base_disabled: or $r14 $r14 - bra e mmctx_multi_disabled + bra e #mmctx_multi_disabled iowr I[$r8 + 0x200] $r14 // MMCTX_MULTI_STRIDE iowr I[$r8 + 0x300] $r15 // MMCTX_MULTI_MASK bset $r9 1 // MULTI_EN @@ -264,7 +264,7 @@ mmctx_xfer: mmctx_wait_free: iord $r14 I[$r8 + 0x000] // MMCTX_CTRL and $r14 0x1f - bra e mmctx_wait_free + bra e #mmctx_wait_free // queue up an entry ld b32 $r14 D[$r12] @@ -272,19 +272,19 @@ mmctx_xfer: iowr I[$r8 + 0x300] $r14 add b32 $r12 4 cmpu b32 $r12 $r13 - bra ne mmctx_exec_loop + bra ne #mmctx_exec_loop xbit $r11 $r10 2 - bra ne mmctx_stop + bra ne #mmctx_stop // wait for queue to empty mmctx_fini_wait: iord $r11 I[$r8 + 0x000] // MMCTX_CTRL and $r11 0x1f cmpu b32 $r11 0x10 - bra ne mmctx_fini_wait + bra ne #mmctx_fini_wait mov $r10 2 // DONE_MMCTX - call wait_donez - bra mmctx_done + call #wait_donez + bra #mmctx_done mmctx_stop: xbit $r11 $r10 0 shl b32 $r11 16 // DIR @@ -295,7 +295,7 @@ mmctx_xfer: // wait for STOP_TRIGGER to clear iord $r11 I[$r8 + 0x000] // MMCTX_CTRL xbit $r11 $r11 18 - bra ne mmctx_stop_wait + bra ne #mmctx_stop_wait mmctx_done: trace_clr(T_MMCTX) ret @@ -305,7 +305,7 @@ mmctx_xfer: strand_wait: push $r10 mov $r10 2 - call wait_donez + call #wait_donez pop $r10 ret @@ -316,7 +316,7 @@ strand_pre: sethi $r8 0x20000 mov $r9 0xc iowr I[$r8] $r9 - call strand_wait + call #strand_wait ret // unknown - call after issuing strand commands @@ -326,7 +326,7 @@ strand_post: sethi $r8 0x20000 mov $r9 0xd iowr I[$r8] $r9 - call strand_wait + call #strand_wait ret // Selects strand set?! @@ -341,11 +341,11 @@ strand_set: iowr I[$r10 + 0x000] $r12 // 0x93c = 0xf mov $r12 0xb iowr I[$r11 + 0x000] $r12 // 0x928 = 0xb - call strand_wait + call #strand_wait iowr I[$r10 + 0x000] $r14 // 0x93c = mov $r12 0xa iowr I[$r11 + 0x000] $r12 // 0x928 = 0xa - call strand_wait + call #strand_wait ret // Initialise strand context data @@ -357,22 +357,22 @@ strand_set: // strand_ctx_init: trace_set(T_STRINIT) - call strand_pre + call #strand_pre mov $r14 3 - call strand_set + call #strand_set mov $r10 0x46fc sethi $r10 0x20000 add b32 $r11 $r10 0x400 iowr I[$r10 + 0x100] $r0 // STRAND_FIRST_GENE = 0 mov $r12 1 iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_FIRST_GENE - call strand_wait + call #strand_wait sub b32 $r12 $r0 1 iowr I[$r10 + 0x000] $r12 // STRAND_GENE_CNT = 0xffffffff mov $r12 2 iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_GENE_CNT - call strand_wait - call strand_post + call #strand_wait + call #strand_post // read the size of each strand, poke the context offset of // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry @@ -391,7 +391,7 @@ strand_ctx_init: add b32 $r14 $r10 add b32 $r8 4 sub b32 $r9 1 - bra ne ctx_init_strand_loop + bra ne #ctx_init_strand_loop shl b32 $r14 8 sub b32 $r15 $r14 $r15 diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc index 06f5e26d1e0f..a9e93c80ef03 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc @@ -32,7 +32,7 @@ * - watchdog timer around ctx operations */ -.section nvc0_grgpc_data +.section #nvc0_grgpc_data include(`nvc0_graph.fuc') gpc_id: .b32 0 gpc_mmio_list_head: .b32 0 @@ -48,40 +48,40 @@ cmd_queue: queue_init // chipset descriptions chipsets: .b8 0xc0 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc0_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc0_tpc_mmio_tail .b8 0xc1 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc1_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc1_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc1_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc1_tpc_mmio_tail .b8 0xc3 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc3_tpc_mmio_tail .b8 0xc4 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc3_tpc_mmio_tail .b8 0xc8 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc0_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc0_tpc_mmio_tail .b8 0xce 0 0 0 -.b16 nvc0_gpc_mmio_head -.b16 nvc0_gpc_mmio_tail -.b16 nvc0_tpc_mmio_head -.b16 nvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.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 +.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 @@ -147,8 +147,8 @@ mmctx_data(0x000544, 1) nvc1_tpc_mmio_tail: -.section nvc0_grgpc_code -bra init +.section #nvc0_grgpc_code +bra #init define(`include_code') include(`nvc0_graph.fuc') @@ -160,10 +160,10 @@ error: push $r14 mov $r14 -0x67ec // 0x9814 sethi $r14 0x400000 - call nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code + call #nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code add b32 $r14 0x41c mov $r15 1 - call nv_wr32 // HUB_CTXCTL_INTR_UP_SET + call #nv_wr32 // HUB_CTXCTL_INTR_UP_SET pop $r14 ret @@ -190,7 +190,7 @@ init: iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE // setup i0 handler, and route all interrupts to it - mov $r1 ih + mov $r1 #ih mov $iv0 $r1 mov $r1 0x400 iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH @@ -210,24 +210,24 @@ init: and $r2 0x1f shl b32 $r3 $r2 sub b32 $r3 1 - st b32 D[$r0 + tpc_count] $r2 - st b32 D[$r0 + tpc_mask] $r3 + st b32 D[$r0 + #tpc_count] $r2 + st b32 D[$r0 + #tpc_mask] $r3 add b32 $r1 0x400 iord $r2 I[$r1 + 0x000] // MYINDEX - st b32 D[$r0 + gpc_id] $r2 + st b32 D[$r0 + #gpc_id] $r2 // find context data for this chipset mov $r2 0x800 shl b32 $r2 6 iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r1 chipsets - 12 + mov $r1 #chipsets - 12 init_find_chipset: add b32 $r1 12 ld b32 $r3 D[$r1 + 0x00] cmpu b32 $r3 $r2 - bra e init_context + bra e #init_context cmpu b32 $r3 0 - bra ne init_find_chipset + bra ne #init_find_chipset // unknown chipset ret @@ -253,19 +253,19 @@ init: clear b32 $r15 ld b16 $r14 D[$r1 + 4] ld b16 $r15 D[$r1 + 6] - st b16 D[$r0 + gpc_mmio_list_head] $r14 - st b16 D[$r0 + gpc_mmio_list_tail] $r15 - call mmctx_size + st b16 D[$r0 + #gpc_mmio_list_head] $r14 + st b16 D[$r0 + #gpc_mmio_list_tail] $r15 + call #mmctx_size add b32 $r2 $r15 add b32 $r3 $r15 // calculate per-TPC mmio context size, store the list pointers ld b16 $r14 D[$r1 + 8] ld b16 $r15 D[$r1 + 10] - st b16 D[$r0 + tpc_mmio_list_head] $r14 - st b16 D[$r0 + tpc_mmio_list_tail] $r15 - call mmctx_size - ld b32 $r14 D[$r0 + tpc_count] + st b16 D[$r0 + #tpc_mmio_list_head] $r14 + st b16 D[$r0 + #tpc_mmio_list_tail] $r15 + call #mmctx_size + ld b32 $r14 D[$r0 + #tpc_count] mulu $r14 $r15 add b32 $r2 $r14 add b32 $r3 $r14 @@ -283,7 +283,7 @@ init: // calculate size of strand context data mov b32 $r15 $r2 - call strand_ctx_init + call #strand_ctx_init add b32 $r3 $r15 // save context size, and tell HUB we're done @@ -301,13 +301,13 @@ init: main: bset $flags $p0 sleep $p0 - mov $r13 cmd_queue - call queue_get - bra $p1 main + mov $r13 #cmd_queue + call #queue_get + bra $p1 #main // 0x0000-0x0003 are all context transfers cmpu b32 $r14 0x04 - bra nc main_not_ctx_xfer + bra nc #main_not_ctx_xfer // fetch $flags and mask off $p1/$p2 mov $r1 $flags mov $r2 0x0006 @@ -318,14 +318,14 @@ main: or $r1 $r14 mov $flags $r1 // transfer context data - call ctx_xfer - bra main + call #ctx_xfer + bra #main main_not_ctx_xfer: shl b32 $r15 $r14 16 or $r15 E_BAD_COMMAND - call error - bra main + call #error + bra #main // interrupt handler ih: @@ -342,13 +342,13 @@ ih: // incoming fifo command? iord $r10 I[$r0 + 0x200] // INTR and $r11 $r10 0x00000004 - bra e ih_no_fifo + bra e #ih_no_fifo // queue incoming fifo command for later processing mov $r11 0x1900 - mov $r13 cmd_queue + mov $r13 #cmd_queue iord $r14 I[$r11 + 0x100] // FIFO_CMD iord $r15 I[$r11 + 0x000] // FIFO_DATA - call queue_put + call #queue_put add b32 $r11 0x400 mov $r14 1 iowr I[$r11 + 0x000] $r14 // FIFO_ACK @@ -374,11 +374,11 @@ ih: // hub_barrier_done: mov $r15 1 - ld b32 $r14 D[$r0 + gpc_id] + ld b32 $r14 D[$r0 + #gpc_id] shl b32 $r15 $r14 mov $r14 -0x6be8 // 0x409418 - HUB_BAR_SET sethi $r14 0x400000 - call nv_wr32 + call #nv_wr32 ret // Disables various things, waits a bit, and re-enables them.. @@ -395,7 +395,7 @@ ctx_redswitch: mov $r15 8 ctx_redswitch_delay: sub b32 $r15 1 - bra ne ctx_redswitch_delay + bra ne #ctx_redswitch_delay mov $r15 0xa20 iowr I[$r14] $r15 // GPC_RED_SWITCH = UNK11, ENABLE, POWER ret @@ -413,8 +413,8 @@ ctx_xfer: mov $r1 0xa04 shl b32 $r1 6 iowr I[$r1 + 0x000] $r15// MEM_BASE - bra not $p1 ctx_xfer_not_load - call ctx_redswitch + bra not $p1 #ctx_xfer_not_load + call #ctx_redswitch ctx_xfer_not_load: // strands @@ -422,7 +422,7 @@ ctx_xfer: sethi $r1 0x20000 mov $r2 0xc iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call strand_wait + call #strand_wait mov $r2 0x47fc sethi $r2 0x20000 iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 @@ -435,46 +435,46 @@ ctx_xfer: or $r10 2 // first mov $r11 0x0000 sethi $r11 0x500000 - ld b32 $r12 D[$r0 + gpc_id] + ld b32 $r12 D[$r0 + #gpc_id] shl b32 $r12 15 add b32 $r11 $r12 // base = NV_PGRAPH_GPCn - ld b32 $r12 D[$r0 + gpc_mmio_list_head] - ld b32 $r13 D[$r0 + gpc_mmio_list_tail] + ld b32 $r12 D[$r0 + #gpc_mmio_list_head] + ld b32 $r13 D[$r0 + #gpc_mmio_list_tail] mov $r14 0 // not multi - call mmctx_xfer + call #mmctx_xfer // per-TPC mmio context xbit $r10 $flags $p1 // direction or $r10 4 // last mov $r11 0x4000 sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0 - ld b32 $r12 D[$r0 + gpc_id] + ld b32 $r12 D[$r0 + #gpc_id] shl b32 $r12 15 add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0 - ld b32 $r12 D[$r0 + tpc_mmio_list_head] - ld b32 $r13 D[$r0 + tpc_mmio_list_tail] - ld b32 $r15 D[$r0 + tpc_mask] + ld b32 $r12 D[$r0 + #tpc_mmio_list_head] + ld b32 $r13 D[$r0 + #tpc_mmio_list_tail] + ld b32 $r15 D[$r0 + #tpc_mask] mov $r14 0x800 // stride = 0x800 - call mmctx_xfer + call #mmctx_xfer // wait for strands to finish - call strand_wait + call #strand_wait // if load, or a save without a load following, do some // unknown stuff that's done after finishing a block of // strand commands - bra $p1 ctx_xfer_post - bra not $p2 ctx_xfer_done + bra $p1 #ctx_xfer_post + bra not $p2 #ctx_xfer_done ctx_xfer_post: mov $r1 0x4afc sethi $r1 0x20000 mov $r2 0xd iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0d - call strand_wait + call #strand_wait // mark completion in HUB's barrier ctx_xfer_done: - call hub_barrier_done + call #hub_barrier_done ret .align 256 diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc index e4f8c7e89ddd..3ea31966ddb0 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc @@ -27,7 +27,7 @@ * m4 nvc0_grhub.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_grhub.fuc.h */ -.section nvc0_grhub_data +.section #nvc0_grhub_data include(`nvc0_graph.fuc') gpc_count: .b32 0 rop_count: .b32 0 @@ -39,26 +39,26 @@ ctx_current: .b32 0 chipsets: .b8 0xc0 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc1 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc1_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc1_hub_mmio_tail .b8 0xc3 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc4 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc8 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xce 0 0 0 -.b16 nvc0_hub_mmio_head -.b16 nvc0_hub_mmio_tail +.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 +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0 0 0 0 nvc0_hub_mmio_head: @@ -113,8 +113,8 @@ chan_mmio_address: .b32 0 .align 256 xfer_data: .b32 0 -.section nvc0_grhub_code -bra init +.section #nvc0_grhub_code +bra #init define(`include_code') include(`nvc0_graph.fuc') @@ -157,7 +157,7 @@ init: iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE // setup i0 handler, and route all interrupts to it - mov $r1 ih + mov $r1 #ih mov $iv0 $r1 mov $r1 0x400 iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH @@ -201,11 +201,11 @@ init: // fetch enabled GPC/ROP counts mov $r14 -0x69fc // 0x409604 sethi $r14 0x400000 - call nv_rd32 + call #nv_rd32 extr $r1 $r15 16:20 - st b32 D[$r0 + rop_count] $r1 + st b32 D[$r0 + #rop_count] $r1 and $r15 0x1f - st b32 D[$r0 + gpc_count] $r15 + st b32 D[$r0 + #gpc_count] $r15 // set BAR_REQMASK to GPC mask mov $r1 1 @@ -220,14 +220,14 @@ init: mov $r2 0x800 shl b32 $r2 6 iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r15 chipsets - 8 + mov $r15 #chipsets - 8 init_find_chipset: add b32 $r15 8 ld b32 $r3 D[$r15 + 0x00] cmpu b32 $r3 $r2 - bra e init_context + bra e #init_context cmpu b32 $r3 0 - bra ne init_find_chipset + bra ne #init_find_chipset // unknown chipset ret @@ -239,9 +239,9 @@ init: ld b16 $r14 D[$r15 + 4] ld b16 $r15 D[$r15 + 6] sethi $r14 0 - st b32 D[$r0 + hub_mmio_list_head] $r14 - st b32 D[$r0 + hub_mmio_list_tail] $r15 - call mmctx_size + st b32 D[$r0 + #hub_mmio_list_head] $r14 + st b32 D[$r0 + #hub_mmio_list_tail] $r15 + call #mmctx_size // set mmctx base addresses now so we don't have to do it later, // they don't (currently) ever change @@ -260,7 +260,7 @@ init: add b32 $r1 1 shl b32 $r1 8 mov b32 $r15 $r1 - call strand_ctx_init + call #strand_ctx_init add b32 $r1 $r15 // initialise each GPC in sequence by passing in the offset of its @@ -271,40 +271,40 @@ init: // when it has completed, and return the size of its context data // in GPCn_CC_SCRATCH[1] // - ld b32 $r3 D[$r0 + gpc_count] + ld b32 $r3 D[$r0 + #gpc_count] mov $r4 0x2000 sethi $r4 0x500000 init_gpc: // setup, and start GPC ucode running add b32 $r14 $r4 0x804 mov b32 $r15 $r1 - call nv_wr32 // CC_SCRATCH[1] = ctx offset + call #nv_wr32 // CC_SCRATCH[1] = ctx offset add b32 $r14 $r4 0x800 mov b32 $r15 $r2 - call nv_wr32 // CC_SCRATCH[0] = chipset + call #nv_wr32 // CC_SCRATCH[0] = chipset add b32 $r14 $r4 0x10c clear b32 $r15 - call nv_wr32 + call #nv_wr32 add b32 $r14 $r4 0x104 - call nv_wr32 // ENTRY + call #nv_wr32 // ENTRY add b32 $r14 $r4 0x100 mov $r15 2 // CTRL_START_TRIGGER - call nv_wr32 // CTRL + call #nv_wr32 // CTRL // wait for it to complete, and adjust context size add b32 $r14 $r4 0x800 init_gpc_wait: - call nv_rd32 + call #nv_rd32 xbit $r15 $r15 31 - bra e init_gpc_wait + bra e #init_gpc_wait add b32 $r14 $r4 0x804 - call nv_rd32 + call #nv_rd32 add b32 $r1 $r15 // next! add b32 $r4 0x8000 sub b32 $r3 1 - bra ne init_gpc + bra ne #init_gpc // save context size, and tell host we're ready mov $r2 0x800 @@ -322,13 +322,13 @@ main: // sleep until we have something to do bset $flags $p0 sleep $p0 - mov $r13 cmd_queue - call queue_get - bra $p1 main + mov $r13 #cmd_queue + call #queue_get + bra $p1 #main // context switch, requested by GPU? cmpu b32 $r14 0x4001 - bra ne main_not_ctx_switch + bra ne #main_not_ctx_switch trace_set(T_AUTO) mov $r1 0xb00 shl b32 $r1 6 @@ -336,39 +336,39 @@ main: iord $r1 I[$r1 + 0x000] // CHAN_CUR xbit $r3 $r1 31 - bra e chsw_no_prev + bra e #chsw_no_prev xbit $r3 $r2 31 - bra e chsw_prev_no_next + bra e #chsw_prev_no_next push $r2 mov b32 $r2 $r1 trace_set(T_SAVE) bclr $flags $p1 bset $flags $p2 - call ctx_xfer + call #ctx_xfer trace_clr(T_SAVE); pop $r2 trace_set(T_LOAD); bset $flags $p1 - call ctx_xfer + call #ctx_xfer trace_clr(T_LOAD); - bra chsw_done + bra #chsw_done chsw_prev_no_next: push $r2 mov b32 $r2 $r1 bclr $flags $p1 bclr $flags $p2 - call ctx_xfer + call #ctx_xfer pop $r2 mov $r1 0xb00 shl b32 $r1 6 iowr I[$r1] $r2 - bra chsw_done + bra #chsw_done chsw_no_prev: xbit $r3 $r2 31 - bra e chsw_done + bra e #chsw_done bset $flags $p1 bclr $flags $p2 - call ctx_xfer + call #ctx_xfer // ack the context switch request chsw_done: @@ -377,32 +377,32 @@ main: mov $r2 1 iowr I[$r1 + 0x000] $r2 // 0x409b0c trace_clr(T_AUTO) - bra main + bra #main // request to set current channel? (*not* a context switch) main_not_ctx_switch: cmpu b32 $r14 0x0001 - bra ne main_not_ctx_chan + bra ne #main_not_ctx_chan mov b32 $r2 $r15 - call ctx_chan - bra main_done + call #ctx_chan + bra #main_done // request to store current channel context? main_not_ctx_chan: cmpu b32 $r14 0x0002 - bra ne main_not_ctx_save + bra ne #main_not_ctx_save trace_set(T_SAVE) bclr $flags $p1 bclr $flags $p2 - call ctx_xfer + call #ctx_xfer trace_clr(T_SAVE) - bra main_done + bra #main_done main_not_ctx_save: shl b32 $r15 $r14 16 or $r15 E_BAD_COMMAND - call error - bra main + call #error + bra #main main_done: mov $r1 0x820 @@ -410,7 +410,7 @@ main: clear b32 $r2 bset $r2 31 iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 - bra main + bra #main // interrupt handler ih: @@ -427,13 +427,13 @@ ih: // incoming fifo command? iord $r10 I[$r0 + 0x200] // INTR and $r11 $r10 0x00000004 - bra e ih_no_fifo + bra e #ih_no_fifo // queue incoming fifo command for later processing mov $r11 0x1900 - mov $r13 cmd_queue + mov $r13 #cmd_queue iord $r14 I[$r11 + 0x100] // FIFO_CMD iord $r15 I[$r11 + 0x000] // FIFO_DATA - call queue_put + call #queue_put add b32 $r11 0x400 mov $r14 1 iowr I[$r11 + 0x000] $r14 // FIFO_ACK @@ -441,18 +441,18 @@ ih: // context switch request? ih_no_fifo: and $r11 $r10 0x00000100 - bra e ih_no_ctxsw + bra e #ih_no_ctxsw // enqueue a context switch for later processing - mov $r13 cmd_queue + mov $r13 #cmd_queue mov $r14 0x4001 - call queue_put + call #queue_put // anything we didn't handle, bring it to the host's attention ih_no_ctxsw: mov $r11 0x104 not b32 $r11 and $r11 $r10 $r11 - bra e ih_no_other + bra e #ih_no_other mov $r10 0xc1c shl b32 $r10 6 iowr I[$r10] $r11 // INTR_UP_SET @@ -478,11 +478,11 @@ ctx_4160s: mov $r14 0x4160 sethi $r14 0x400000 mov $r15 1 - call nv_wr32 + call #nv_wr32 ctx_4160s_wait: - call nv_rd32 + call #nv_rd32 xbit $r15 $r15 4 - bra e ctx_4160s_wait + bra e #ctx_4160s_wait ret // Without clearing again at end of xfer, some things cause PGRAPH @@ -492,7 +492,7 @@ ctx_4160c: mov $r14 0x4160 sethi $r14 0x400000 clear b32 $r15 - call nv_wr32 + call #nv_wr32 ret // Again, not real sure @@ -503,7 +503,7 @@ ctx_4170s: mov $r14 0x4170 sethi $r14 0x400000 or $r15 0x10 - call nv_wr32 + call #nv_wr32 ret // Waits for a ctx_4170s() call to complete @@ -511,9 +511,9 @@ ctx_4170s: ctx_4170w: mov $r14 0x4170 sethi $r14 0x400000 - call nv_rd32 + call #nv_rd32 and $r15 0x10 - bra ne ctx_4170w + bra ne #ctx_4170w ret // Disables various things, waits a bit, and re-enables them.. @@ -530,7 +530,7 @@ ctx_redswitch: mov $r15 8 ctx_redswitch_delay: sub b32 $r15 1 - bra ne ctx_redswitch_delay + bra ne #ctx_redswitch_delay mov $r15 0x770 iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL ret @@ -546,10 +546,10 @@ ctx_86c: iowr I[$r14] $r15 // HUB(0x86c) = val mov $r14 -0x75ec sethi $r14 0x400000 - call nv_wr32 // ROP(0xa14) = val + call #nv_wr32 // ROP(0xa14) = val mov $r14 -0x5794 sethi $r14 0x410000 - call nv_wr32 // GPC(0x86c) = val + call #nv_wr32 // GPC(0x86c) = val ret // ctx_load - load's a channel's ctxctl data, and selects its vm @@ -561,7 +561,7 @@ ctx_load: // switch to channel, somewhat magic in parts.. mov $r10 12 // DONE_UNK12 - call wait_donez + call #wait_donez mov $r1 0xa24 shl b32 $r1 6 iowr I[$r1 + 0x000] $r0 // 0x409a24 @@ -576,7 +576,7 @@ ctx_load: ctx_chan_wait_0: iord $r4 I[$r1 + 0x100] and $r4 0x1f - bra ne ctx_chan_wait_0 + bra ne #ctx_chan_wait_0 iowr I[$r3 + 0x000] $r2 // CHAN_CUR // load channel header, fetch PGRAPH context pointer @@ -595,19 +595,19 @@ ctx_load: sethi $r2 0x80000000 iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vram mov $r1 0x10 // chan + 0x0210 - mov $r2 xfer_data + mov $r2 #xfer_data sethi $r2 0x00020000 // 16 bytes xdld $r1 $r2 xdwait trace_clr(T_LCHAN) // update current context - ld b32 $r1 D[$r0 + xfer_data + 4] + ld b32 $r1 D[$r0 + #xfer_data + 4] shl b32 $r1 24 - ld b32 $r2 D[$r0 + xfer_data + 0] + ld b32 $r2 D[$r0 + #xfer_data + 0] shr b32 $r2 8 or $r1 $r2 - st b32 D[$r0 + ctx_current] $r1 + st b32 D[$r0 + #ctx_current] $r1 // set transfer base to start of context, and fetch context header trace_set(T_LCTXH) @@ -618,7 +618,7 @@ ctx_load: mov $r1 0xa20 shl b32 $r1 6 iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vm - mov $r1 chan_data + mov $r1 #chan_data sethi $r1 0x00060000 // 256 bytes xdld $r0 $r1 xdwait @@ -635,10 +635,10 @@ ctx_load: // In: $r2 channel address // ctx_chan: - call ctx_4160s - call ctx_load + call #ctx_4160s + call #ctx_load mov $r10 12 // DONE_UNK12 - call wait_donez + call #wait_donez mov $r1 0xa10 shl b32 $r1 6 mov $r2 5 @@ -646,8 +646,8 @@ ctx_chan: ctx_chan_wait: iord $r2 I[$r1 + 0x000] or $r2 $r2 - bra ne ctx_chan_wait - call ctx_4160c + bra ne #ctx_chan_wait + call #ctx_4160c ret // Execute per-context state overrides list @@ -661,7 +661,7 @@ ctx_chan: // ctx_mmio_exec: // set transfer base to be the mmio list - ld b32 $r3 D[$r0 + chan_mmio_address] + ld b32 $r3 D[$r0 + #chan_mmio_address] mov $r2 0xa04 shl b32 $r2 6 iowr I[$r2 + 0x000] $r3 // MEM_BASE @@ -670,31 +670,31 @@ ctx_mmio_exec: ctx_mmio_loop: // fetch next 256 bytes of mmio list if necessary and $r4 $r3 0xff - bra ne ctx_mmio_pull - mov $r5 xfer_data + bra ne #ctx_mmio_pull + mov $r5 #xfer_data sethi $r5 0x00060000 // 256 bytes xdld $r3 $r5 xdwait // execute a single list entry ctx_mmio_pull: - ld b32 $r14 D[$r4 + xfer_data + 0x00] - ld b32 $r15 D[$r4 + xfer_data + 0x04] - call nv_wr32 + ld b32 $r14 D[$r4 + #xfer_data + 0x00] + ld b32 $r15 D[$r4 + #xfer_data + 0x04] + call #nv_wr32 // next! add b32 $r3 8 sub b32 $r1 1 - bra ne ctx_mmio_loop + bra ne #ctx_mmio_loop // set transfer base back to the current context ctx_mmio_done: - ld b32 $r3 D[$r0 + ctx_current] + ld b32 $r3 D[$r0 + #ctx_current] iowr I[$r2 + 0x000] $r3 // MEM_BASE // disable the mmio list now, we don't need/want to execute it again - st b32 D[$r0 + chan_mmio_count] $r0 - mov $r1 chan_data + st b32 D[$r0 + #chan_mmio_count] $r0 + mov $r1 #chan_data sethi $r1 0x00060000 // 256 bytes xdst $r0 $r1 xdwait @@ -709,46 +709,46 @@ ctx_mmio_exec: // on load it means: "a save preceeded this load" // ctx_xfer: - bra not $p1 ctx_xfer_pre - bra $p2 ctx_xfer_pre_load + bra not $p1 #ctx_xfer_pre + bra $p2 #ctx_xfer_pre_load ctx_xfer_pre: mov $r15 0x10 - call ctx_86c - call ctx_4160s - bra not $p1 ctx_xfer_exec + call #ctx_86c + call #ctx_4160s + bra not $p1 #ctx_xfer_exec ctx_xfer_pre_load: mov $r15 2 - call ctx_4170s - call ctx_4170w - call ctx_redswitch + call #ctx_4170s + call #ctx_4170w + call #ctx_redswitch clear b32 $r15 - call ctx_4170s - call ctx_load + call #ctx_4170s + call #ctx_load // fetch context pointer, and initiate xfer on all GPCs ctx_xfer_exec: - ld b32 $r1 D[$r0 + ctx_current] + ld b32 $r1 D[$r0 + #ctx_current] mov $r2 0x414 shl b32 $r2 6 iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset mov $r14 -0x5b00 sethi $r14 0x410000 mov b32 $r15 $r1 - call nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer + call #nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer add b32 $r14 4 xbit $r15 $flags $p1 xbit $r2 $flags $p2 shl b32 $r2 1 or $r15 $r2 - call nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) + call #nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) // strands mov $r1 0x4afc sethi $r1 0x20000 mov $r2 0xc iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call strand_wait + call #strand_wait mov $r2 0x47fc sethi $r2 0x20000 iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 @@ -760,22 +760,22 @@ ctx_xfer: xbit $r10 $flags $p1 // direction or $r10 6 // first, last mov $r11 0 // base = 0 - ld b32 $r12 D[$r0 + hub_mmio_list_head] - ld b32 $r13 D[$r0 + hub_mmio_list_tail] + ld b32 $r12 D[$r0 + #hub_mmio_list_head] + ld b32 $r13 D[$r0 + #hub_mmio_list_tail] mov $r14 0 // not multi - call mmctx_xfer + call #mmctx_xfer // wait for GPCs to all complete mov $r10 8 // DONE_BAR - call wait_doneo + call #wait_doneo // wait for strand xfer to complete - call strand_wait + call #strand_wait // post-op - bra $p1 ctx_xfer_post + bra $p1 #ctx_xfer_post mov $r10 12 // DONE_UNK12 - call wait_donez + call #wait_donez mov $r1 0xa10 shl b32 $r1 6 mov $r2 5 @@ -783,27 +783,27 @@ ctx_xfer: ctx_xfer_post_save_wait: iord $r2 I[$r1] or $r2 $r2 - bra ne ctx_xfer_post_save_wait + bra ne #ctx_xfer_post_save_wait - bra $p2 ctx_xfer_done + bra $p2 #ctx_xfer_done ctx_xfer_post: mov $r15 2 - call ctx_4170s + call #ctx_4170s clear b32 $r15 - call ctx_86c - call strand_post - call ctx_4170w + call #ctx_86c + call #strand_post + call #ctx_4170w clear b32 $r15 - call ctx_4170s + call #ctx_4170s - bra not $p1 ctx_xfer_no_post_mmio - ld b32 $r1 D[$r0 + chan_mmio_count] + bra not $p1 #ctx_xfer_no_post_mmio + ld b32 $r1 D[$r0 + #chan_mmio_count] or $r1 $r1 - bra e ctx_xfer_no_post_mmio - call ctx_mmio_exec + bra e #ctx_xfer_no_post_mmio + call #ctx_mmio_exec ctx_xfer_no_post_mmio: - call ctx_4160c + call #ctx_4160c ctx_xfer_done: ret -- cgit v1.2.3 From 06784090ecb3f925616fc797164a74b03d5c0968 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 11 Jul 2011 15:57:54 +1000 Subject: drm/nvc0/gr: add initial support for nvd9, not quite there yet.. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 5 +- drivers/gpu/drm/nouveau/nvc0_graph.c | 6 +- drivers/gpu/drm/nouveau/nvc0_graph.h | 1 + drivers/gpu/drm/nouveau/nvc0_grctx.c | 127 ++++++++++++++++++++++++------- drivers/gpu/drm/nouveau/nvc0_grgpc.fuc | 59 ++++++++++++++ drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h | 80 +++++++++++++++---- drivers/gpu/drm/nouveau/nvc0_grhub.fuc | 45 +++++++++++ drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h | 96 +++++++++++------------ 8 files changed, 326 insertions(+), 93 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 3e3798f7f369..46831fea7861 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -655,6 +655,7 @@ nouveau_card_init(struct drm_device *dev) nv50_graph_create(dev); break; case NV_C0: + case NV_D0: nvc0_graph_create(dev); break; default: @@ -1111,13 +1112,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) dev_priv->noaccel = !!nouveau_noaccel; if (nouveau_noaccel == -1) { switch (dev_priv->chipset) { -#if 0 - case 0xXX: /* known broken */ + case 0xd9: /* known broken */ NV_INFO(dev, "acceleration disabled by default, pass " "noaccel=0 to force enable\n"); dev_priv->noaccel = true; break; -#endif default: dev_priv->noaccel = false; break; diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index ecfafd70cf0e..8ee3963f9030 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -875,14 +875,16 @@ nvc0_graph_create(struct drm_device *dev) case 0xcf: /* 4/0/0/0, 3 */ priv->magic_not_rop_nr = 0x03; break; + case 0xd9: /* 1/0/0/0, 1 */ + priv->magic_not_rop_nr = 0x01; + break; } if (!priv->magic_not_rop_nr) { NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n", priv->tp_nr[0], priv->tp_nr[1], priv->tp_nr[2], priv->tp_nr[3], priv->rop_nr); - /* use 0xc3's values... */ - priv->magic_not_rop_nr = 0x03; + priv->magic_not_rop_nr = 0x00; } NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */ diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h index 636fe9812f79..91d44ea662d9 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.h +++ b/drivers/gpu/drm/nouveau/nvc0_graph.h @@ -87,6 +87,7 @@ nvc0_graph_class(struct drm_device *dev) case 0xc1: return 0x9197; case 0xc8: + case 0xd9: return 0x9297; default: return 0; diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c index 96b0b93d94ca..de77842b31c0 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grctx.c +++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c @@ -1268,6 +1268,17 @@ nvc0_grctx_generate_9039(struct drm_device *dev) static void nvc0_grctx_generate_90c0(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + int i; + + for (i = 0; dev_priv->chipset == 0xd9 && i < 4; i++) { + nv_mthd(dev, 0x90c0, 0x2700 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2720 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2704 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2724 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2708 + (i * 0x40), 0x00000000); + nv_mthd(dev, 0x90c0, 0x2728 + (i * 0x40), 0x00000000); + } nv_mthd(dev, 0x90c0, 0x270c, 0x00000000); nv_mthd(dev, 0x90c0, 0x272c, 0x00000000); nv_mthd(dev, 0x90c0, 0x274c, 0x00000000); @@ -1276,6 +1287,12 @@ nvc0_grctx_generate_90c0(struct drm_device *dev) nv_mthd(dev, 0x90c0, 0x27ac, 0x00000000); nv_mthd(dev, 0x90c0, 0x27cc, 0x00000000); nv_mthd(dev, 0x90c0, 0x27ec, 0x00000000); + for (i = 0; dev_priv->chipset == 0xd9 && i < 4; i++) { + nv_mthd(dev, 0x90c0, 0x2710 + (i * 0x40), 0x00014000); + nv_mthd(dev, 0x90c0, 0x2730 + (i * 0x40), 0x00014000); + nv_mthd(dev, 0x90c0, 0x2714 + (i * 0x40), 0x00000040); + nv_mthd(dev, 0x90c0, 0x2734 + (i * 0x40), 0x00000040); + } nv_mthd(dev, 0x90c0, 0x030c, 0x00000001); nv_mthd(dev, 0x90c0, 0x1944, 0x00000000); nv_mthd(dev, 0x90c0, 0x0758, 0x00000100); @@ -1471,14 +1488,20 @@ nvc0_grctx_generate_shaders(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - if (dev_priv->chipset != 0xc1) { - nv_wr32(dev, 0x405800, 0x078000bf); - nv_wr32(dev, 0x405830, 0x02180000); - } else { + if (dev_priv->chipset == 0xd9) { nv_wr32(dev, 0x405800, 0x0f8000bf); nv_wr32(dev, 0x405830, 0x02180218); + nv_wr32(dev, 0x405834, 0x08000000); + } else + if (dev_priv->chipset == 0xc1) { + nv_wr32(dev, 0x405800, 0x0f8000bf); + nv_wr32(dev, 0x405830, 0x02180218); + nv_wr32(dev, 0x405834, 0x00000000); + } else { + nv_wr32(dev, 0x405800, 0x078000bf); + nv_wr32(dev, 0x405830, 0x02180000); + nv_wr32(dev, 0x405834, 0x00000000); } - nv_wr32(dev, 0x405834, 0x00000000); nv_wr32(dev, 0x405838, 0x00000000); nv_wr32(dev, 0x405854, 0x00000000); nv_wr32(dev, 0x405870, 0x00000001); @@ -1509,7 +1532,10 @@ nvc0_grctx_generate_unk64xx(struct drm_device *dev) nv_wr32(dev, 0x4064ac, 0x00003fff); nv_wr32(dev, 0x4064b4, 0x00000000); nv_wr32(dev, 0x4064b8, 0x00000000); - if (dev_priv->chipset == 0xc1) { + if (dev_priv->chipset == 0xd9) + nv_wr32(dev, 0x4064bc, 0x00000000); + if (dev_priv->chipset == 0xc1 || + dev_priv->chipset == 0xd9) { nv_wr32(dev, 0x4064c0, 0x80140078); nv_wr32(dev, 0x4064c4, 0x0086ffff); } @@ -1550,10 +1576,23 @@ nvc0_grctx_generate_rop(struct drm_device *dev) /* ROPC_BROADCAST */ nv_wr32(dev, 0x408800, 0x02802a3c); nv_wr32(dev, 0x408804, 0x00000040); - nv_wr32(dev, 0x408808, chipset != 0xc1 ? 0x0003e00d : 0x1003e005); - nv_wr32(dev, 0x408900, 0x3080b801); - nv_wr32(dev, 0x408904, chipset != 0xc1 ? 0x02000001 : 0x62000001); - nv_wr32(dev, 0x408908, 0x00c80929); + if (chipset == 0xd9) { + nv_wr32(dev, 0x408808, 0x1043e005); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, 0x1043e005); + nv_wr32(dev, 0x408908, 0x00c8102f); + } else + if (chipset == 0xc1) { + nv_wr32(dev, 0x408808, 0x1003e005); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, 0x62000001); + nv_wr32(dev, 0x408908, 0x00c80929); + } else { + nv_wr32(dev, 0x408808, 0x0003e00d); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, 0x02000001); + nv_wr32(dev, 0x408908, 0x00c80929); + } nv_wr32(dev, 0x40890c, 0x00000000); nv_wr32(dev, 0x408980, 0x0000011d); } @@ -1572,7 +1611,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418408, 0x00000000); nv_wr32(dev, 0x41840c, 0x00001008); nv_wr32(dev, 0x418410, 0x0fff0fff); - nv_wr32(dev, 0x418414, 0x00200fff); + nv_wr32(dev, 0x418414, chipset != 0xd9 ? 0x00200fff : 0x02200fff); nv_wr32(dev, 0x418450, 0x00000000); nv_wr32(dev, 0x418454, 0x00000000); nv_wr32(dev, 0x418458, 0x00000000); @@ -1587,14 +1626,17 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418700, 0x00000002); nv_wr32(dev, 0x418704, 0x00000080); nv_wr32(dev, 0x418708, 0x00000000); - nv_wr32(dev, 0x41870c, 0x07c80000); + nv_wr32(dev, 0x41870c, chipset != 0xd9 ? 0x07c80000 : 0x00000000); nv_wr32(dev, 0x418710, 0x00000000); - nv_wr32(dev, 0x418800, 0x0006860a); + nv_wr32(dev, 0x418800, chipset != 0xd9 ? 0x0006860a : 0x7006860a); nv_wr32(dev, 0x418808, 0x00000000); nv_wr32(dev, 0x41880c, 0x00000000); nv_wr32(dev, 0x418810, 0x00000000); nv_wr32(dev, 0x418828, 0x00008442); - nv_wr32(dev, 0x418830, chipset != 0xc1 ? 0x00000001 : 0x10000001); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x418830, 0x10000001); + else + nv_wr32(dev, 0x418830, 0x00000001); nv_wr32(dev, 0x4188d8, 0x00000008); nv_wr32(dev, 0x4188e0, 0x01000000); nv_wr32(dev, 0x4188e8, 0x00000000); @@ -1602,7 +1644,12 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x4188f0, 0x00000000); nv_wr32(dev, 0x4188f4, 0x00000000); nv_wr32(dev, 0x4188f8, 0x00000000); - nv_wr32(dev, 0x4188fc, chipset != 0xc1 ? 0x00100000 : 0x00100018); + if (chipset == 0xd9) + nv_wr32(dev, 0x4188fc, 0x20100008); + else if (chipset == 0xc1) + nv_wr32(dev, 0x4188fc, 0x00100018); + else + nv_wr32(dev, 0x4188fc, 0x00100000); nv_wr32(dev, 0x41891c, 0x00ff00ff); nv_wr32(dev, 0x418924, 0x00000000); nv_wr32(dev, 0x418928, 0x00ffff00); @@ -1616,7 +1663,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418a14 + (i * 0x20), 0x00000000); nv_wr32(dev, 0x418a18 + (i * 0x20), 0x00000000); } - nv_wr32(dev, 0x418b00, 0x00000000); + nv_wr32(dev, 0x418b00, chipset != 0xd9 ? 0x00000000 : 0x00000006); nv_wr32(dev, 0x418b08, 0x0a418820); nv_wr32(dev, 0x418b0c, 0x062080e6); nv_wr32(dev, 0x418b10, 0x020398a4); @@ -1633,7 +1680,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev) nv_wr32(dev, 0x418c24, 0x00000000); nv_wr32(dev, 0x418c28, 0x00000000); nv_wr32(dev, 0x418c2c, 0x00000000); - if (chipset == 0xc1) + if (chipset == 0xc1 || chipset == 0xd9) nv_wr32(dev, 0x418c6c, 0x00000001); nv_wr32(dev, 0x418c80, 0x20200004); nv_wr32(dev, 0x418c8c, 0x00000001); @@ -1653,7 +1700,10 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419818, 0x00000000); nv_wr32(dev, 0x41983c, 0x00038bc7); nv_wr32(dev, 0x419848, 0x00000000); - nv_wr32(dev, 0x419864, chipset != 0xc1 ? 0x0000012a : 0x00000129); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x419864, 0x00000129); + else + nv_wr32(dev, 0x419864, 0x0000012a); nv_wr32(dev, 0x419888, 0x00000000); nv_wr32(dev, 0x419a00, 0x000001f0); nv_wr32(dev, 0x419a04, 0x00000001); @@ -1663,7 +1713,9 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419a14, 0x00000200); nv_wr32(dev, 0x419a1c, 0x00000000); nv_wr32(dev, 0x419a20, 0x00000800); - if (chipset != 0xc0 && chipset != 0xc8) + if (chipset == 0xd9) + nv_wr32(dev, 0x00419ac4, 0x0017f440); + else if (chipset != 0xc0 && chipset != 0xc8) nv_wr32(dev, 0x00419ac4, 0x0007f440); nv_wr32(dev, 0x419b00, 0x0a418820); nv_wr32(dev, 0x419b04, 0x062080e6); @@ -1672,21 +1724,33 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419b10, 0x0a418820); nv_wr32(dev, 0x419b14, 0x000000e6); nv_wr32(dev, 0x419bd0, 0x00900103); - nv_wr32(dev, 0x419be0, chipset != 0xc1 ? 0x00000001 : 0x00400001); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x419be0, 0x00400001); + else + nv_wr32(dev, 0x419be0, 0x00000001); nv_wr32(dev, 0x419be4, 0x00000000); - nv_wr32(dev, 0x419c00, 0x00000002); + nv_wr32(dev, 0x419c00, chipset != 0xd9 ? 0x00000002 : 0x0000000a); nv_wr32(dev, 0x419c04, 0x00000006); nv_wr32(dev, 0x419c08, 0x00000002); nv_wr32(dev, 0x419c20, 0x00000000); - if (chipset == 0xce || chipset == 0xcf) + if (dev_priv->chipset == 0xd9) { + nv_wr32(dev, 0x419c24, 0x00084210); + nv_wr32(dev, 0x419c28, 0x3cf3cf3c); nv_wr32(dev, 0x419cb0, 0x00020048); - else + } else + 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); + if (chipset == 0xc1 || chipset == 0xd9) + nv_wr32(dev, 0x419d20, 0x12180000); + else + nv_wr32(dev, 0x419d20, 0x02180000); nv_wr32(dev, 0x419d24, 0x00001fff); - if (chipset == 0xc1) + if (chipset == 0xc1 || chipset == 0xd9) nv_wr32(dev, 0x419d44, 0x02180218); nv_wr32(dev, 0x419e04, 0x00000000); nv_wr32(dev, 0x419e08, 0x00000000); @@ -1986,6 +2050,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x00000215, 0x00000040); nv_icmd(dev, 0x00000216, 0x00000040); nv_icmd(dev, 0x00000217, 0x00000040); + if (dev_priv->chipset == 0xd9) { + for (i = 0x0400; i <= 0x0417; i++) + nv_icmd(dev, i, 0x00000040); + } nv_icmd(dev, 0x00000218, 0x0000c080); nv_icmd(dev, 0x00000219, 0x0000c080); nv_icmd(dev, 0x0000021a, 0x0000c080); @@ -1994,6 +2062,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x0000021d, 0x0000c080); nv_icmd(dev, 0x0000021e, 0x0000c080); nv_icmd(dev, 0x0000021f, 0x0000c080); + if (dev_priv->chipset == 0xd9) { + for (i = 0x0440; i <= 0x0457; i++) + nv_icmd(dev, i, 0x0000c080); + } nv_icmd(dev, 0x000000ad, 0x0000013e); nv_icmd(dev, 0x000000e1, 0x00000010); nv_icmd(dev, 0x00000290, 0x00000000); @@ -2556,7 +2628,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x0000053f, 0xffff0000); nv_icmd(dev, 0x00000585, 0x0000003f); nv_icmd(dev, 0x00000576, 0x00000003); - if (dev_priv->chipset == 0xc1) + if (dev_priv->chipset == 0xc1 || + dev_priv->chipset == 0xd9) nv_icmd(dev, 0x0000057b, 0x00000059); nv_icmd(dev, 0x00000586, 0x00000040); nv_icmd(dev, 0x00000582, 0x00000080); @@ -2658,6 +2731,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_icmd(dev, 0x00000957, 0x00000003); nv_icmd(dev, 0x0000095e, 0x20164010); nv_icmd(dev, 0x0000095f, 0x00000020); + if (dev_priv->chipset == 0xd9) + nv_icmd(dev, 0x0000097d, 0x00000020); nv_icmd(dev, 0x00000683, 0x00000006); nv_icmd(dev, 0x00000685, 0x003fffff); nv_icmd(dev, 0x00000687, 0x00000c48); diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc index a9e93c80ef03..15272be33b66 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc @@ -82,6 +82,11 @@ chipsets: .b16 #nvc0_gpc_mmio_tail .b16 #nvc0_tpc_mmio_head .b16 #nvcf_tpc_mmio_tail +.b8 0xd9 0 0 0 +.b16 #nvd9_gpc_mmio_head +.b16 #nvd9_gpc_mmio_tail +.b16 #nvd9_tpc_mmio_head +.b16 #nvd9_tpc_mmio_tail .b8 0 0 0 0 // GPC mmio lists @@ -114,6 +119,35 @@ nvc0_gpc_mmio_tail: mmctx_data(0x000c6c, 1); nvc1_gpc_mmio_tail: +nvd9_gpc_mmio_head: +mmctx_data(0x000380, 1) +mmctx_data(0x000400, 2) +mmctx_data(0x00040c, 3) +mmctx_data(0x000450, 9) +mmctx_data(0x000600, 1) +mmctx_data(0x000684, 1) +mmctx_data(0x000700, 5) +mmctx_data(0x000800, 1) +mmctx_data(0x000808, 3) +mmctx_data(0x000828, 1) +mmctx_data(0x000830, 1) +mmctx_data(0x0008d8, 1) +mmctx_data(0x0008e0, 1) +mmctx_data(0x0008e8, 6) +mmctx_data(0x00091c, 1) +mmctx_data(0x000924, 3) +mmctx_data(0x000b00, 1) +mmctx_data(0x000b08, 6) +mmctx_data(0x000bb8, 1) +mmctx_data(0x000c08, 1) +mmctx_data(0x000c10, 8) +mmctx_data(0x000c6c, 1) +mmctx_data(0x000c80, 1) +mmctx_data(0x000c8c, 1) +mmctx_data(0x001000, 3) +mmctx_data(0x001014, 1) +nvd9_gpc_mmio_tail: + // TPC mmio lists nvc0_tpc_mmio_head: mmctx_data(0x000018, 1) @@ -146,6 +180,31 @@ nvc3_tpc_mmio_tail: mmctx_data(0x000544, 1) nvc1_tpc_mmio_tail: +nvd9_tpc_mmio_head: +mmctx_data(0x000018, 1) +mmctx_data(0x00003c, 1) +mmctx_data(0x000048, 1) +mmctx_data(0x000064, 1) +mmctx_data(0x000088, 1) +mmctx_data(0x000200, 6) +mmctx_data(0x00021c, 2) +mmctx_data(0x0002c4, 1) +mmctx_data(0x000300, 6) +mmctx_data(0x0003d0, 1) +mmctx_data(0x0003e0, 2) +mmctx_data(0x000400, 3) +mmctx_data(0x000420, 3) +mmctx_data(0x0004b0, 1) +mmctx_data(0x0004e8, 1) +mmctx_data(0x0004f4, 1) +mmctx_data(0x000520, 2) +mmctx_data(0x000544, 1) +mmctx_data(0x000604, 4) +mmctx_data(0x000644, 20) +mmctx_data(0x000698, 1) +mmctx_data(0x0006e0, 1) +mmctx_data(0x000750, 3) +nvd9_tpc_mmio_tail: .section #nvc0_grgpc_code bra #init diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h index 6f820324480e..a988b8ad00ac 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h @@ -25,26 +25,29 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, 0x00000000, 0x000000c0, - 0x011c00bc, - 0x01700120, + 0x012800c8, + 0x01e40194, 0x000000c1, - 0x012000bc, - 0x01840120, + 0x012c00c8, + 0x01f80194, 0x000000c3, - 0x011c00bc, - 0x01800120, + 0x012800c8, + 0x01f40194, 0x000000c4, - 0x011c00bc, - 0x01800120, + 0x012800c8, + 0x01f40194, 0x000000c8, - 0x011c00bc, - 0x01700120, + 0x012800c8, + 0x01e40194, 0x000000ce, - 0x011c00bc, - 0x01800120, + 0x012800c8, + 0x01f40194, 0x000000cf, - 0x011c00bc, - 0x017c0120, + 0x012800c8, + 0x01f00194, + 0x000000d9, + 0x0194012c, + 0x025401f8, 0x00000000, 0x00000380, 0x14000400, @@ -71,6 +74,32 @@ uint32_t nvc0_grgpc_data[] = { 0x08001000, 0x00001014, 0x00000c6c, + 0x00000380, + 0x04000400, + 0x0800040c, + 0x20000450, + 0x00000600, + 0x00000684, + 0x10000700, + 0x00000800, + 0x08000808, + 0x00000828, + 0x00000830, + 0x000008d8, + 0x000008e0, + 0x140008e8, + 0x0000091c, + 0x08000924, + 0x00000b00, + 0x14000b08, + 0x00000bb8, + 0x00000c08, + 0x1c000c10, + 0x00000c6c, + 0x00000c80, + 0x00000c8c, + 0x08001000, + 0x00001014, 0x00000018, 0x0000003c, 0x00000048, @@ -96,6 +125,29 @@ uint32_t nvc0_grgpc_data[] = { 0x000006e0, 0x000004bc, 0x00000544, + 0x00000018, + 0x0000003c, + 0x00000048, + 0x00000064, + 0x00000088, + 0x14000200, + 0x0400021c, + 0x000002c4, + 0x14000300, + 0x000003d0, + 0x040003e0, + 0x08000400, + 0x08000420, + 0x000004b0, + 0x000004e8, + 0x000004f4, + 0x04000520, + 0x00000544, + 0x0c000604, + 0x4c000644, + 0x00000698, + 0x000006e0, + 0x08000750, }; uint32_t nvc0_grgpc_code[] = { diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc index 3ea31966ddb0..98acddb2c5bb 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc @@ -59,6 +59,9 @@ chipsets: .b8 0xcf 0 0 0 .b16 #nvc0_hub_mmio_head .b16 #nvc0_hub_mmio_tail +.b8 0xd9 0 0 0 +.b16 #nvd9_hub_mmio_head +.b16 #nvd9_hub_mmio_tail .b8 0 0 0 0 nvc0_hub_mmio_head: @@ -105,6 +108,48 @@ nvc0_hub_mmio_tail: mmctx_data(0x4064c0, 2) nvc1_hub_mmio_tail: +nvd9_hub_mmio_head: +mmctx_data(0x17e91c, 2) +mmctx_data(0x400204, 2) +mmctx_data(0x404004, 10) +mmctx_data(0x404044, 1) +mmctx_data(0x404094, 14) +mmctx_data(0x4040d0, 7) +mmctx_data(0x4040f8, 1) +mmctx_data(0x404130, 3) +mmctx_data(0x404150, 3) +mmctx_data(0x404164, 2) +mmctx_data(0x404178, 2) +mmctx_data(0x404200, 8) +mmctx_data(0x404404, 14) +mmctx_data(0x404460, 4) +mmctx_data(0x404480, 1) +mmctx_data(0x404498, 1) +mmctx_data(0x404604, 4) +mmctx_data(0x404618, 32) +mmctx_data(0x404698, 21) +mmctx_data(0x4046f0, 2) +mmctx_data(0x404700, 22) +mmctx_data(0x405800, 1) +mmctx_data(0x405830, 3) +mmctx_data(0x405854, 1) +mmctx_data(0x405870, 4) +mmctx_data(0x405a00, 2) +mmctx_data(0x405a18, 1) +mmctx_data(0x406020, 1) +mmctx_data(0x406028, 4) +mmctx_data(0x4064a8, 2) +mmctx_data(0x4064b4, 5) +mmctx_data(0x407804, 1) +mmctx_data(0x40780c, 6) +mmctx_data(0x4078bc, 1) +mmctx_data(0x408000, 7) +mmctx_data(0x408064, 1) +mmctx_data(0x408800, 3) +mmctx_data(0x408900, 4) +mmctx_data(0x408980, 1) +nvd9_hub_mmio_tail: + .align 256 chan_data: chan_mmio_count: .b32 0 diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h index 241d3263f1e5..c5ed307abeb9 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h @@ -23,19 +23,21 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, 0x00000000, 0x000000c0, - 0x01340098, + 0x013c00a0, 0x000000c1, - 0x01380098, + 0x014000a0, 0x000000c3, - 0x01340098, + 0x013c00a0, 0x000000c4, - 0x01340098, + 0x013c00a0, 0x000000c8, - 0x01340098, + 0x013c00a0, 0x000000ce, - 0x01340098, + 0x013c00a0, 0x000000cf, - 0x01340098, + 0x013c00a0, + 0x000000d9, + 0x01dc0140, 0x00000000, 0x0417e91c, 0x04400204, @@ -77,47 +79,45 @@ uint32_t nvc0_grhub_data[] = { 0x0c408900, 0x00408980, 0x044064c0, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x0417e91c, + 0x04400204, + 0x24404004, + 0x00404044, + 0x34404094, + 0x184040d0, + 0x004040f8, + 0x08404130, + 0x08404150, + 0x04404164, + 0x04404178, + 0x1c404200, + 0x34404404, + 0x0c404460, + 0x00404480, + 0x00404498, + 0x0c404604, + 0x7c404618, + 0x50404698, + 0x044046f0, + 0x54404700, + 0x00405800, + 0x08405830, + 0x00405854, + 0x0c405870, + 0x04405a00, + 0x00405a18, + 0x00406020, + 0x0c406028, + 0x044064a8, + 0x104064b4, + 0x00407804, + 0x1440780c, + 0x004078bc, + 0x18408000, + 0x00408064, + 0x08408800, + 0x0c408900, + 0x00408980, 0x00000000, 0x00000000, 0x00000000, -- cgit v1.2.3 From 6805979fa9090b2c69c645cf19bba9a2849f336b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 30 Oct 2011 23:04:31 +1000 Subject: drm/nv50/pm: 0x84/0x86 can't use "1" for nvclk src, need 0x50 method Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 53b6c6c75744..ab8cfad5a9b0 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -562,8 +562,11 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) } nv_mask(dev, 0x00c040, 0x0c000c00, info->amast); - /* core/shader: switch core to dom6, shader to hclk */ - if (dev_priv->chipset == 0x50) + /* core/shader: make sure sclk/nvclk are disconnected from their + * plls (nvclk to dom6, sclk to hclk), modify the plls, and + * reconnect sclk/nvclk to their new clock source + */ + if (dev_priv->chipset < 0x92) nv_mask(dev, 0x00c040, 0x001000b0, 0x00100080); /* grrr! */ else nv_mask(dev, 0x00c040, 0x000000b3, 0x00000081); -- cgit v1.2.3 From 463464eb9bf7d0254f3effa8050246fa4e009ace Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 30 Oct 2011 23:10:55 +1000 Subject: drm/nv50/pm: fix thinko which lead to clocks being slightly off sometimes read_pll_ref() needs to take into account the refclk src bits in 0xc040 on some chipsets, it wasn't doing this. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 46 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index ab8cfad5a9b0..9d61bd45c47e 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -66,7 +66,7 @@ read_div(struct drm_device *dev) } static u32 -read_pll_ref(struct drm_device *dev, u32 base) +read_pll_src(struct drm_device *dev, u32 base) { struct drm_nouveau_private *dev_priv = dev->dev_private; u32 coef, ref = read_clk(dev, clk_src_crystal); @@ -137,21 +137,12 @@ read_pll_ref(struct drm_device *dev, u32 base) } static u32 -read_pll(struct drm_device *dev, u32 base) +read_pll_ref(struct drm_device *dev, u32 base) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - u32 mast = nv_rd32(dev, 0x00c040); - u32 src = 0, ref = 0, clk = 0; - u32 ctrl, coef; - int N1, N2, M1, M2; + u32 src, mast = nv_rd32(dev, 0x00c040); switch (base) { case 0x004028: - if (mast & 0x00100000) { - /* wtf, appears to only disable post-divider on nva0 */ - if (dev_priv->chipset != 0xa0) - return read_clk(dev, clk_src_dom6); - } src = !!(mast & 0x00200000); break; case 0x004020: @@ -164,22 +155,33 @@ read_pll(struct drm_device *dev, u32 base) src = !!(mast & 0x02000000); break; case 0x00e810: - ref = read_clk(dev, clk_src_crystal); - break; + return read_clk(dev, clk_src_crystal); default: NV_ERROR(dev, "bad pll 0x%06x\n", base); return 0; } - if (ref == 0) { - if (src) - ref = read_clk(dev, clk_src_href); - else - ref = read_pll_ref(dev, base); - } + if (src) + return read_clk(dev, clk_src_href); + return read_pll_src(dev, base); +} - ctrl = nv_rd32(dev, base + 0); - coef = nv_rd32(dev, base + 4); +static u32 +read_pll(struct drm_device *dev, u32 base) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mast = nv_rd32(dev, 0x00c040); + u32 ctrl = nv_rd32(dev, base + 0); + u32 coef = nv_rd32(dev, base + 4); + u32 ref = read_pll_ref(dev, base); + u32 clk = 0; + int N1, N2, M1, M2; + + if (base == 0x004028 && (mast & 0x00100000)) { + /* wtf, appears to only disable post-divider on nva0 */ + if (dev_priv->chipset != 0xa0) + return read_clk(dev, clk_src_dom6); + } N2 = (coef & 0xff000000) >> 24; M2 = (coef & 0x00ff0000) >> 16; -- cgit v1.2.3 From 973e861657f6faaa3ee01de093d2307470a6d193 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 31 Oct 2011 10:52:33 +1000 Subject: drm/nv50/pm: avoid touching dom6/vdec clocks if perflvl doesn't define it Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 9d61bd45c47e..0b82c6075666 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -485,7 +485,8 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) * clock domains work, i suspect at least some of them can also be * tied to xpll... */ - info->amast = info->pdivs = 0; + info->amast = nv_rd32(dev, 0x00c040); + info->pdivs = read_div(dev); if (perflvl->vdec) { /* see how close we can get using nvclk as a source */ clk = calc_div(perflvl->core, perflvl->vdec, &P1); @@ -498,12 +499,12 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) out = calc_div(out, perflvl->vdec, &P2); /* select whichever gets us closest */ + info->amast &= ~0x00000c00; + info->pdivs &= ~0x00000700; if (abs((int)perflvl->vdec - clk) <= abs((int)perflvl->vdec - out)) { if (dev_priv->chipset != 0x98) info->amast |= 0x00000c00; - else - info->amast |= 0x00000000; info->pdivs |= P1 << 8; } else { info->amast |= 0x00000800; @@ -514,23 +515,22 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) /* dom6: nfi what this is, but we're limited to various combinations * of the host clock frequency */ - if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) { - info->amast |= 0x00000000; - info->pdivs |= read_div(dev) & 0x00000007; - } else - if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) { - info->amast |= 0x08000000; - info->pdivs |= read_div(dev) & 0x00000007; - } else if (perflvl->dom6) { - clk = read_clk(dev, clk_src_hclk) * 3; - clk = calc_div(clk, perflvl->dom6, &P1); + info->amast &= ~0x0c000000; + if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) { + info->amast |= 0x00000000; + } else + if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) { + info->amast |= 0x08000000; + } else { + clk = read_clk(dev, clk_src_hclk) * 3; + clk = calc_div(clk, perflvl->dom6, &P1); - info->amast |= 0x0c000000; - info->pdivs |= P1; + info->amast |= 0x0c000000; + info->pdivs = (info->pdivs & ~0x00000007) | P1; + } } - return info; error: kfree(info); -- cgit v1.2.3 From 8b5f4d0def9caa16527c95e7a4ba47bb8a4d9e1e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 31 Oct 2011 11:59:07 +1000 Subject: drm/nv50/pm: stabilise transition to 100MHz mclk a bit Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 0b82c6075666..961d8f20d27f 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -465,7 +465,7 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) /* memory: use pcie refclock if possible, otherwise use mpll */ info->mscript = perflvl->memscript; if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) { - info->mctrl = nv_rd32(dev, 0x4008) | 0x00000200; + info->mctrl = 0x00000200 | (pll.log2p_bias << 19); info->mcoef = nv_rd32(dev, 0x400c); } else if (perflvl->memory) { @@ -606,7 +606,7 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) /* modify mpll */ nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000); - nv_mask(dev, 0x004008, 0x81ff0200, 0x00000200 | info->mctrl); + nv_mask(dev, 0x004008, 0x01ff0200, 0x00000200 | info->mctrl); nv_wr32(dev, 0x00400c, info->mcoef); udelay(100); nv_mask(dev, 0x004008, 0x81ff0200, info->mctrl); -- cgit v1.2.3 From d2491567cdbcb87b2682e0948a69d73c4dd8987e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 31 Oct 2011 12:14:10 +1000 Subject: drm/nv50/pm: only touch 0x611200 on nv92- Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 961d8f20d27f..e7ff5acce65f 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -594,8 +594,10 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) } /* disable display */ - nv_wr32(dev, 0x611200, 0x00003300); - udelay(100); + if (dev_priv->chipset >= 0x92) { + nv_wr32(dev, 0x611200, 0x00003300); + udelay(100); + } /* prepare ram for reclocking */ nv_wr32(dev, 0x1002d4, 0x00000001); /* precharge */ @@ -617,7 +619,8 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) udelay(100); /* re-enable display */ - nv_wr32(dev, 0x611200, 0x00003330); + if (dev_priv->chipset >= 0x92) + nv_wr32(dev, 0x611200, 0x00003330); goto resume; error: -- cgit v1.2.3 From 971fa6b46df2edc02c9937938d9ba1d6d6724e74 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 5 Nov 2011 11:55:39 +1000 Subject: drm/nva3/copy: update fuc source for latest envytools Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_copy.fuc | 260 +++++++++++++++++----------------- 1 file changed, 131 insertions(+), 129 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc b/drivers/gpu/drm/nouveau/nva3_copy.fuc index eaf35f8321ee..0cec534f03e9 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.fuc +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc @@ -31,8 +31,9 @@ */ ifdef(`NVA3', -.section nva3_pcopy_data, -.section nvc0_pcopy_data +.section #nva3_pcopy_data +, +.section #nvc0_pcopy_data ) ctx_object: .b32 0 @@ -42,7 +43,7 @@ ctx_dma_query: .b32 0 ctx_dma_src: .b32 0 ctx_dma_dst: .b32 0 ,) -.equ ctx_dma_count 3 +.equ #ctx_dma_count 3 ctx_query_address_high: .b32 0 ctx_query_address_low: .b32 0 ctx_query_counter: .b32 0 @@ -78,64 +79,65 @@ ctx_ycnt: .b32 0 dispatch_table: // mthd 0x0000, NAME .b16 0x000 1 -.b32 ctx_object ~0xffffffff +.b32 #ctx_object ~0xffffffff // mthd 0x0100, NOP .b16 0x040 1 -.b32 0x00010000 + cmd_nop ~0xffffffff +.b32 0x00010000 + #cmd_nop ~0xffffffff // mthd 0x0140, PM_TRIGGER .b16 0x050 1 -.b32 0x00010000 + cmd_pm_trigger ~0xffffffff +.b32 0x00010000 + #cmd_pm_trigger ~0xffffffff ifdef(`NVA3', ` // mthd 0x0180-0x018c, DMA_ -.b16 0x060 ctx_dma_count +.b16 0x060 #ctx_dma_count dispatch_dma: -.b32 0x00010000 + cmd_dma ~0xffffffff -.b32 0x00010000 + cmd_dma ~0xffffffff -.b32 0x00010000 + cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff ',) // mthd 0x0200-0x0218, SRC_TILE .b16 0x80 7 -.b32 ctx_src_tile_mode ~0x00000fff -.b32 ctx_src_xsize ~0x0007ffff -.b32 ctx_src_ysize ~0x00001fff -.b32 ctx_src_zsize ~0x000007ff -.b32 ctx_src_zoff ~0x00000fff -.b32 ctx_src_xoff ~0x0007ffff -.b32 ctx_src_yoff ~0x00001fff +.b32 #ctx_src_tile_mode ~0x00000fff +.b32 #ctx_src_xsize ~0x0007ffff +.b32 #ctx_src_ysize ~0x00001fff +.b32 #ctx_src_zsize ~0x000007ff +.b32 #ctx_src_zoff ~0x00000fff +.b32 #ctx_src_xoff ~0x0007ffff +.b32 #ctx_src_yoff ~0x00001fff // mthd 0x0220-0x0238, DST_TILE .b16 0x88 7 -.b32 ctx_dst_tile_mode ~0x00000fff -.b32 ctx_dst_xsize ~0x0007ffff -.b32 ctx_dst_ysize ~0x00001fff -.b32 ctx_dst_zsize ~0x000007ff -.b32 ctx_dst_zoff ~0x00000fff -.b32 ctx_dst_xoff ~0x0007ffff -.b32 ctx_dst_yoff ~0x00001fff +.b32 #ctx_dst_tile_mode ~0x00000fff +.b32 #ctx_dst_xsize ~0x0007ffff +.b32 #ctx_dst_ysize ~0x00001fff +.b32 #ctx_dst_zsize ~0x000007ff +.b32 #ctx_dst_zoff ~0x00000fff +.b32 #ctx_dst_xoff ~0x0007ffff +.b32 #ctx_dst_yoff ~0x00001fff // mthd 0x0300-0x0304, EXEC, WRCACHE_FLUSH .b16 0xc0 2 -.b32 0x00010000 + cmd_exec ~0xffffffff -.b32 0x00010000 + cmd_wrcache_flush ~0xffffffff +.b32 0x00010000 + #cmd_exec ~0xffffffff +.b32 0x00010000 + #cmd_wrcache_flush ~0xffffffff // mthd 0x030c-0x0340, various stuff .b16 0xc3 14 -.b32 ctx_src_address_high ~0x000000ff -.b32 ctx_src_address_low ~0xfffffff0 -.b32 ctx_dst_address_high ~0x000000ff -.b32 ctx_dst_address_low ~0xfffffff0 -.b32 ctx_src_pitch ~0x0007ffff -.b32 ctx_dst_pitch ~0x0007ffff -.b32 ctx_xcnt ~0x0000ffff -.b32 ctx_ycnt ~0x00001fff -.b32 ctx_format ~0x0333ffff -.b32 ctx_swz_const0 ~0xffffffff -.b32 ctx_swz_const1 ~0xffffffff -.b32 ctx_query_address_high ~0x000000ff -.b32 ctx_query_address_low ~0xffffffff -.b32 ctx_query_counter ~0xffffffff +.b32 #ctx_src_address_high ~0x000000ff +.b32 #ctx_src_address_low ~0xfffffff0 +.b32 #ctx_dst_address_high ~0x000000ff +.b32 #ctx_dst_address_low ~0xfffffff0 +.b32 #ctx_src_pitch ~0x0007ffff +.b32 #ctx_dst_pitch ~0x0007ffff +.b32 #ctx_xcnt ~0x0000ffff +.b32 #ctx_ycnt ~0x00001fff +.b32 #ctx_format ~0x0333ffff +.b32 #ctx_swz_const0 ~0xffffffff +.b32 #ctx_swz_const1 ~0xffffffff +.b32 #ctx_query_address_high ~0x000000ff +.b32 #ctx_query_address_low ~0xffffffff +.b32 #ctx_query_counter ~0xffffffff .b16 0x800 0 ifdef(`NVA3', -.section nva3_pcopy_code, -.section nvc0_pcopy_code +.section #nva3_pcopy_code +, +.section #nvc0_pcopy_code ) main: @@ -143,7 +145,7 @@ main: mov $sp $r0 // setup i0 handler and route fifo and ctxswitch to it - mov $r1 ih + mov $r1 #ih mov $iv0 $r1 mov $r1 0x400 movw $r2 0xfff3 @@ -164,19 +166,19 @@ main: bset $flags $p0 spin: sleep $p0 - bra spin + bra #spin // i0 handler ih: iord $r1 I[$r0 + 0x200] and $r2 $r1 0x00000008 - bra e ih_no_chsw - call chsw + bra e #ih_no_chsw + call #chsw ih_no_chsw: and $r2 $r1 0x00000004 - bra e ih_no_cmd - call dispatch + bra e #ih_no_cmd + call #dispatch ih_no_cmd: and $r1 $r1 0x0000000c @@ -235,9 +237,9 @@ ifdef(`NVA3', ` sethi $r4 0x60000 // swap! - bra $p1 swctx_load + bra $p1 #swctx_load xdst $r0 $r4 - bra swctx_done + bra #swctx_done swctx_load: xdld $r0 $r4 swctx_done: @@ -251,9 +253,9 @@ chsw: // if it's active, unload it and return xbit $r15 $r3 0x1e - bra e chsw_no_unload + bra e #chsw_no_unload bclr $flags $p1 - call swctx + call #swctx bclr $r3 0x1e iowr I[$r2] $r3 mov $r4 1 @@ -266,20 +268,20 @@ chsw: // is there a channel waiting to be loaded? xbit $r13 $r3 0x1e - bra e chsw_finish_load + bra e #chsw_finish_load bset $flags $p1 - call swctx + call #swctx ifdef(`NVA3', // load dma objects back into TARGET regs - mov $r5 ctx_dma - mov $r6 ctx_dma_count + mov $r5 #ctx_dma + mov $r6 #ctx_dma_count chsw_load_ctx_dma: ld b32 $r7 D[$r5 + $r6 * 4] add b32 $r8 $r6 0x180 shl b32 $r8 8 iowr I[$r8] $r7 sub b32 $r6 1 - bra nc chsw_load_ctx_dma + bra nc #chsw_load_ctx_dma ,) chsw_finish_load: @@ -297,7 +299,7 @@ dispatch: shl b32 $r2 0x10 // lookup method in the dispatch table, ILLEGAL_MTHD if not found - mov $r5 dispatch_table + mov $r5 #dispatch_table clear b32 $r6 clear b32 $r7 dispatch_loop: @@ -305,14 +307,14 @@ dispatch: ld b16 $r7 D[$r5 + 2] add b32 $r5 4 cmpu b32 $r4 $r6 - bra c dispatch_illegal_mthd + bra c #dispatch_illegal_mthd add b32 $r7 $r6 cmpu b32 $r4 $r7 - bra c dispatch_valid_mthd + bra c #dispatch_valid_mthd sub b32 $r7 $r6 shl b32 $r7 3 add b32 $r5 $r7 - bra dispatch_loop + bra #dispatch_loop // ensure no bits set in reserved fields, INVALID_BITFIELD dispatch_valid_mthd: @@ -322,20 +324,20 @@ dispatch: ld b32 $r5 D[$r4 + 4] and $r5 $r3 cmpu b32 $r5 0 - bra ne dispatch_invalid_bitfield + bra ne #dispatch_invalid_bitfield // depending on dispatch flags: execute method, or save data as state ld b16 $r5 D[$r4 + 0] ld b16 $r6 D[$r4 + 2] cmpu b32 $r6 0 - bra ne dispatch_cmd + bra ne #dispatch_cmd st b32 D[$r5] $r3 - bra dispatch_done + bra #dispatch_done dispatch_cmd: bclr $flags $p1 call $r5 - bra $p1 dispatch_error - bra dispatch_done + bra $p1 #dispatch_error + bra #dispatch_done dispatch_invalid_bitfield: or $r2 2 @@ -353,7 +355,7 @@ dispatch: iord $r2 I[$r0 + 0x200] and $r2 0x40 cmpu b32 $r2 0 - bra ne hostirq_wait + bra ne #hostirq_wait dispatch_done: mov $r2 0x1d00 @@ -409,10 +411,10 @@ ifdef(`NVA3', // $r2: hostirq state // $r3: data cmd_dma: - sub b32 $r4 dispatch_dma + sub b32 $r4 #dispatch_dma shr b32 $r4 1 bset $r3 0x1e - st b32 D[$r4 + ctx_dma] $r3 + st b32 D[$r4 + #ctx_dma] $r3 add b32 $r4 0x600 shl b32 $r4 6 iowr I[$r4] $r3 @@ -430,7 +432,7 @@ cmd_exec_set_format: st b32 D[$sp + 0x0c] $r0 // extract cpp, src_ncomp and dst_ncomp from FORMAT - ld b32 $r4 D[$r0 + ctx_format] + ld b32 $r4 D[$r0 + #ctx_format] extr $r5 $r4 16:17 add b32 $r5 1 extr $r6 $r4 20:21 @@ -448,22 +450,22 @@ cmd_exec_set_format: clear b32 $r11 bpc_loop: cmpu b8 $r10 4 - bra nc cmp_c0 + bra nc #cmp_c0 mulu $r12 $r10 $r5 add b32 $r12 $r11 bset $flags $p2 - bra bpc_next + bra #bpc_next cmp_c0: - bra ne cmp_c1 + bra ne #cmp_c1 mov $r12 0x10 add b32 $r12 $r11 - bra bpc_next + bra #bpc_next cmp_c1: cmpu b8 $r10 6 - bra nc cmp_zero + bra nc #cmp_zero mov $r12 0x14 add b32 $r12 $r11 - bra bpc_next + bra #bpc_next cmp_zero: mov $r12 0x80 bpc_next: @@ -471,22 +473,22 @@ cmd_exec_set_format: add b32 $r8 1 add b32 $r11 1 cmpu b32 $r11 $r5 - bra c bpc_loop + bra c #bpc_loop add b32 $r9 1 cmpu b32 $r9 $r7 - bra c ncomp_loop + bra c #ncomp_loop // SRC_XCNT = (xcnt * src_cpp), or 0 if no src ref in swz (hw will hang) mulu $r6 $r5 - st b32 D[$r0 + ctx_src_cpp] $r6 - ld b32 $r8 D[$r0 + ctx_xcnt] + st b32 D[$r0 + #ctx_src_cpp] $r6 + ld b32 $r8 D[$r0 + #ctx_xcnt] mulu $r6 $r8 - bra $p2 dst_xcnt + bra $p2 #dst_xcnt clear b32 $r6 dst_xcnt: mulu $r7 $r5 - st b32 D[$r0 + ctx_dst_cpp] $r7 + st b32 D[$r0 + #ctx_dst_cpp] $r7 mulu $r7 $r8 mov $r5 0x810 @@ -494,10 +496,10 @@ cmd_exec_set_format: iowr I[$r5 + 0x000] $r6 iowr I[$r5 + 0x100] $r7 add b32 $r5 0x800 - ld b32 $r6 D[$r0 + ctx_dst_cpp] + ld b32 $r6 D[$r0 + #ctx_dst_cpp] sub b32 $r6 1 shl b32 $r6 8 - ld b32 $r7 D[$r0 + ctx_src_cpp] + ld b32 $r7 D[$r0 + #ctx_src_cpp] sub b32 $r7 1 or $r6 $r7 iowr I[$r5 + 0x000] $r6 @@ -511,9 +513,9 @@ cmd_exec_set_format: ld b32 $r6 D[$sp + 0x0c] iowr I[$r5 + 0x300] $r6 add b32 $r5 0x400 - ld b32 $r6 D[$r0 + ctx_swz_const0] + ld b32 $r6 D[$r0 + #ctx_swz_const0] iowr I[$r5 + 0x000] $r6 - ld b32 $r6 D[$r0 + ctx_swz_const1] + ld b32 $r6 D[$r0 + #ctx_swz_const1] iowr I[$r5 + 0x100] $r6 add $sp 0x10 ret @@ -543,7 +545,7 @@ cmd_exec_set_format: // cmd_exec_set_surface_tiled: // translate TILE_MODE into Tp, Th, Td shift values - ld b32 $r7 D[$r5 + ctx_src_tile_mode] + ld b32 $r7 D[$r5 + #ctx_src_tile_mode] extr $r9 $r7 8:11 extr $r8 $r7 4:7 ifdef(`NVA3', @@ -553,9 +555,9 @@ ifdef(`NVA3', ) extr $r7 $r7 0:3 cmp b32 $r7 0xe - bra ne xtile64 + bra ne #xtile64 mov $r7 4 - bra xtileok + bra #xtileok xtile64: xbit $r7 $flags $p2 add b32 $r7 17 @@ -565,8 +567,8 @@ ifdef(`NVA3', // Op = (x * cpp) & ((1 << Tp) - 1) // Tx = (x * cpp) >> Tp - ld b32 $r10 D[$r5 + ctx_src_xoff] - ld b32 $r11 D[$r5 + ctx_src_cpp] + ld b32 $r10 D[$r5 + #ctx_src_xoff] + ld b32 $r11 D[$r5 + #ctx_src_cpp] mulu $r10 $r11 mov $r11 1 shl b32 $r11 $r7 @@ -576,7 +578,7 @@ ifdef(`NVA3', // Tyo = y & ((1 << Th) - 1) // Ty = y >> Th - ld b32 $r13 D[$r5 + ctx_src_yoff] + ld b32 $r13 D[$r5 + #ctx_src_yoff] mov $r14 1 shl b32 $r14 $r8 sub b32 $r14 1 @@ -598,8 +600,8 @@ ifdef(`NVA3', add b32 $r12 $r11 // nTx = ((w * cpp) + ((1 << Tp) - 1) >> Tp) - ld b32 $r15 D[$r5 + ctx_src_xsize] - ld b32 $r11 D[$r5 + ctx_src_cpp] + ld b32 $r15 D[$r5 + #ctx_src_xsize] + ld b32 $r11 D[$r5 + #ctx_src_cpp] mulu $r15 $r11 mov $r11 1 shl b32 $r11 $r7 @@ -609,7 +611,7 @@ ifdef(`NVA3', push $r15 // nTy = (h + ((1 << Th) - 1)) >> Th - ld b32 $r15 D[$r5 + ctx_src_ysize] + ld b32 $r15 D[$r5 + #ctx_src_ysize] mov $r11 1 shl b32 $r11 $r8 sub b32 $r11 1 @@ -629,7 +631,7 @@ ifdef(`NVA3', // Tz = z >> Td // Op += Tzo << Tys // Ts = Tys + Td - ld b32 $r8 D[$r5 + ctx_src_zoff] + ld b32 $r8 D[$r5 + #ctx_src_zoff] mov $r14 1 shl b32 $r14 $r9 sub b32 $r14 1 @@ -656,8 +658,8 @@ ifdef(`NVA3', // SRC_ADDRESS_LOW = (Ot + Op) & 0xffffffff // CFG_ADDRESS_HIGH |= ((Ot + Op) >> 32) << 16 - ld b32 $r7 D[$r5 + ctx_src_address_low] - ld b32 $r8 D[$r5 + ctx_src_address_high] + ld b32 $r7 D[$r5 + #ctx_src_address_low] + ld b32 $r8 D[$r5 + #ctx_src_address_high] add b32 $r10 $r12 add b32 $r7 $r10 adc b32 $r8 0 @@ -677,14 +679,14 @@ cmd_exec_set_surface_linear: xbit $r6 $flags $p2 add b32 $r6 0x202 shl b32 $r6 8 - ld b32 $r7 D[$r5 + ctx_src_address_low] + ld b32 $r7 D[$r5 + #ctx_src_address_low] iowr I[$r6 + 0x000] $r7 add b32 $r6 0x400 - ld b32 $r7 D[$r5 + ctx_src_address_high] + ld b32 $r7 D[$r5 + #ctx_src_address_high] shl b32 $r7 16 iowr I[$r6 + 0x000] $r7 add b32 $r6 0x400 - ld b32 $r7 D[$r5 + ctx_src_pitch] + ld b32 $r7 D[$r5 + #ctx_src_pitch] iowr I[$r6 + 0x000] $r7 ret @@ -697,7 +699,7 @@ cmd_exec_wait: loop: iord $r1 I[$r0] and $r1 1 - bra ne loop + bra ne #loop pop $r1 pop $r0 ret @@ -705,18 +707,18 @@ cmd_exec_wait: cmd_exec_query: // if QUERY_SHORT not set, write out { -, 0, TIME_LO, TIME_HI } xbit $r4 $r3 13 - bra ne query_counter - call cmd_exec_wait + bra ne #query_counter + call #cmd_exec_wait mov $r4 0x80c shl b32 $r4 6 - ld b32 $r5 D[$r0 + ctx_query_address_low] + ld b32 $r5 D[$r0 + #ctx_query_address_low] add b32 $r5 4 iowr I[$r4 + 0x000] $r5 iowr I[$r4 + 0x100] $r0 mov $r5 0xc iowr I[$r4 + 0x200] $r5 add b32 $r4 0x400 - ld b32 $r5 D[$r0 + ctx_query_address_high] + ld b32 $r5 D[$r0 + #ctx_query_address_high] shl b32 $r5 16 iowr I[$r4 + 0x000] $r5 add b32 $r4 0x500 @@ -741,16 +743,16 @@ cmd_exec_query: // write COUNTER query_counter: - call cmd_exec_wait + call #cmd_exec_wait mov $r4 0x80c shl b32 $r4 6 - ld b32 $r5 D[$r0 + ctx_query_address_low] + ld b32 $r5 D[$r0 + #ctx_query_address_low] iowr I[$r4 + 0x000] $r5 iowr I[$r4 + 0x100] $r0 mov $r5 0x4 iowr I[$r4 + 0x200] $r5 add b32 $r4 0x400 - ld b32 $r5 D[$r0 + ctx_query_address_high] + ld b32 $r5 D[$r0 + #ctx_query_address_high] shl b32 $r5 16 iowr I[$r4 + 0x000] $r5 add b32 $r4 0x500 @@ -759,7 +761,7 @@ cmd_exec_query: mov $r5 0x00001110 sethi $r5 0x13120000 iowr I[$r4 + 0x100] $r5 - ld b32 $r5 D[$r0 + ctx_query_counter] + ld b32 $r5 D[$r0 + #ctx_query_counter] add b32 $r4 0x500 iowr I[$r4 + 0x000] $r5 mov $r5 0x00002601 @@ -787,22 +789,22 @@ cmd_exec_query: // $r2: hostirq state // $r3: data cmd_exec: - call cmd_exec_wait + call #cmd_exec_wait // if format requested, call function to calculate it, otherwise // fill in cpp/xcnt for both surfaces as if (cpp == 1) xbit $r15 $r3 0 - bra e cmd_exec_no_format - call cmd_exec_set_format + bra e #cmd_exec_no_format + call #cmd_exec_set_format mov $r4 0x200 - bra cmd_exec_init_src_surface + bra #cmd_exec_init_src_surface cmd_exec_no_format: mov $r6 0x810 shl b32 $r6 6 mov $r7 1 - st b32 D[$r0 + ctx_src_cpp] $r7 - st b32 D[$r0 + ctx_dst_cpp] $r7 - ld b32 $r7 D[$r0 + ctx_xcnt] + st b32 D[$r0 + #ctx_src_cpp] $r7 + st b32 D[$r0 + #ctx_dst_cpp] $r7 + ld b32 $r7 D[$r0 + #ctx_xcnt] iowr I[$r6 + 0x000] $r7 iowr I[$r6 + 0x100] $r7 clear b32 $r4 @@ -811,28 +813,28 @@ cmd_exec: bclr $flags $p2 clear b32 $r5 xbit $r15 $r3 4 - bra e src_tiled - call cmd_exec_set_surface_linear - bra cmd_exec_init_dst_surface + bra e #src_tiled + call #cmd_exec_set_surface_linear + bra #cmd_exec_init_dst_surface src_tiled: - call cmd_exec_set_surface_tiled + call #cmd_exec_set_surface_tiled bset $r4 7 cmd_exec_init_dst_surface: bset $flags $p2 - mov $r5 ctx_dst_address_high - ctx_src_address_high + mov $r5 #ctx_dst_address_high - #ctx_src_address_high xbit $r15 $r3 8 - bra e dst_tiled - call cmd_exec_set_surface_linear - bra cmd_exec_kick + bra e #dst_tiled + call #cmd_exec_set_surface_linear + bra #cmd_exec_kick dst_tiled: - call cmd_exec_set_surface_tiled + call #cmd_exec_set_surface_tiled bset $r4 8 cmd_exec_kick: mov $r5 0x800 shl b32 $r5 6 - ld b32 $r6 D[$r0 + ctx_ycnt] + ld b32 $r6 D[$r0 + #ctx_ycnt] iowr I[$r5 + 0x100] $r6 mov $r6 0x0041 // SRC_TARGET = 1, DST_TARGET = 2 @@ -842,8 +844,8 @@ cmd_exec: // if requested, queue up a QUERY write after the copy has completed xbit $r15 $r3 12 - bra e cmd_exec_done - call cmd_exec_query + bra e #cmd_exec_done + call #cmd_exec_query cmd_exec_done: ret -- cgit v1.2.3 From 0f0f7be8bdfc31f21b4a6b7e02a63b1b26b00123 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 5 Nov 2011 12:08:05 +1000 Subject: drm/nva3/copy: fix typo in fuc which caused host to not recieve exceptions Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_copy.fuc | 2 +- drivers/gpu/drm/nouveau/nva3_copy.fuc.h | 2 +- drivers/gpu/drm/nouveau/nvc0_copy.fuc.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc b/drivers/gpu/drm/nouveau/nva3_copy.fuc index 0cec534f03e9..abc36626fef0 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.fuc +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc @@ -150,7 +150,7 @@ main: mov $r1 0x400 movw $r2 0xfff3 sethi $r2 0 - iowr I[$r2 + 0x300] $r2 + iowr I[$r1 + 0x300] $r2 // enable interrupts or $r2 0xc diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h index 2731de22ebe9..1f33fbdc00be 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h +++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h @@ -152,7 +152,7 @@ uint32_t nva3_pcopy_code[] = { 0xf10010fe, 0xf1040017, 0xf0fff327, - 0x22d00023, + 0x12d00023, 0x0c25f0c0, 0xf40012d0, 0x17f11031, diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h index 419903880e9d..a8d17458ced1 100644 --- a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h @@ -145,7 +145,7 @@ uint32_t nvc0_pcopy_code[] = { 0xf10010fe, 0xf1040017, 0xf0fff327, - 0x22d00023, + 0x12d00023, 0x0c25f0c0, 0xf40012d0, 0x17f11031, -- cgit v1.2.3 From 13f90122e87e30615d0e877337a76bf020c7f043 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Sun, 6 Nov 2011 20:32:03 +0100 Subject: drm/nouveau: print correct engine number which failed to unload/idle Signed-off-by: Marcin Slusarz Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index c033a0a4f2b2..ffde896b1f3c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -222,7 +222,7 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) ret = dev_priv->eng[e]->fini(dev, e, true); if (ret) { - NV_ERROR(dev, "... engine %d failed: %d\n", i, ret); + NV_ERROR(dev, "... engine %d failed: %d\n", e, ret); goto out_abort; } } -- cgit v1.2.3 From 9e7f96aa3a4b26db4a0ac49a96a9b55f1685e8b2 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Sun, 6 Nov 2011 20:32:06 +0100 Subject: drm/nv50: fix page faulting for 128MB page table sizes This seems to be a typo... Signed-off-by: Marcin Slusarz Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c index 40b84f22d819..6f38ceae3aa4 100644 --- a/drivers/gpu/drm/nouveau/nv50_vm.c +++ b/drivers/gpu/drm/nouveau/nv50_vm.c @@ -48,7 +48,7 @@ nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, phys |= 0x60; else if (coverage <= 64 * 1024 * 1024) phys |= 0x40; - else if (coverage < 128 * 1024 * 1024) + else if (coverage <= 128 * 1024 * 1024) phys |= 0x20; } -- cgit v1.2.3 From 50a01fe06e25b271661c6691bc0907ad5ca2c718 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 9 Nov 2011 10:03:01 +1000 Subject: drm/nouveau/hdmi: enable audio for nva3:nvd0 chipsets Pre-nva3 will likely require far more extensive setup, and nvd9 needs to be checked to find its SOR_HDMI/SOR_AUDIO blocks. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_hdmi.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c index 3b4120f0626a..59ea1c14eca0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c +++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c @@ -219,6 +219,8 @@ nouveau_hdmi_mode_set(struct drm_encoder *encoder, { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + u32 max_ac_packet, rekey; nv_connector = nouveau_encoder_connector_get(nv_encoder); if (!mode || !nv_connector || !nv_connector->edid || @@ -227,11 +229,30 @@ nouveau_hdmi_mode_set(struct drm_encoder *encoder, return; } - /* enable hdmi */ - hdmi_mask(encoder, 0x0a4, 0x40000000, 0x40000000); - nouveau_hdmi_video_infoframe(encoder, mode); nouveau_hdmi_audio_infoframe(encoder, mode); + hdmi_mask(encoder, 0x0d0, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ + hdmi_mask(encoder, 0x068, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ + hdmi_mask(encoder, 0x078, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ + + nv_mask(dev, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nv_mask(dev, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nv_mask(dev, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* value matches nvidia binary driver, and tegra constant */ + rekey = 56; + + max_ac_packet = mode->htotal - mode->hdisplay; + max_ac_packet -= rekey; + max_ac_packet -= 18; /* constant from tegra */ + max_ac_packet /= 32; + + /* enable hdmi */ + hdmi_mask(encoder, 0x0a4, 0x5f1f003f, 0x40000000 | /* enable */ + 0x1f000000 | /* unknown */ + max_ac_packet << 16 | + rekey); + nouveau_audio_mode_set(encoder, mode); } -- cgit v1.2.3 From 78951d2226e13d4991073f1a81c4dc13aaf4a4fc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 18:13:13 +1000 Subject: drm/nvd0/disp: send eld to the audio codec Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 66 ++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index e9da42c73371..ceb59802d694 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -765,6 +765,61 @@ nvd0_dac_create(struct drm_connector *connector, struct dcb_entry *dcbe) return 0; } +/****************************************************************************** + * Audio + *****************************************************************************/ +static void +nvd0_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + int i, or = nv_encoder->or * 0x30; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_monitor_audio(nv_connector->edid)) + return; + + nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000001); + + drm_edid_to_eld(&nv_connector->base, nv_connector->edid); + if (nv_connector->base.eld[0]) { + u8 *eld = nv_connector->base.eld; + + for (i = 0; i < eld[2] * 4; i++) + nv_wr32(dev, 0x10ec00 + or, (i << 8) | eld[i]); + for (i = eld[2] * 4; i < 0x60; i++) + nv_wr32(dev, 0x10ec00 + or, (i << 8) | 0x00); + + nv_mask(dev, 0x10ec10 + or, 0x80000002, 0x80000002); + } +} + +static void +nvd0_audio_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + int or = nv_encoder->or * 0x30; + + nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000000); +} + +/****************************************************************************** + * HDMI + *****************************************************************************/ +static void +nvd0_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + nvd0_audio_mode_set(encoder, mode); +} + +static void +nvd0_hdmi_disconnect(struct drm_encoder *encoder) +{ + nvd0_audio_disconnect(encoder); +} + /****************************************************************************** * SOR *****************************************************************************/ @@ -835,7 +890,8 @@ static void 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 drm_device *dev = encoder->dev; + struct drm_nouveau_private *dev_priv = 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; @@ -858,6 +914,8 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, or_config = (mode_ctrl & 0x00000f00) >> 8; if (mode->clock >= 165000) or_config |= 0x0100; + + nvd0_hdmi_mode_set(encoder, mode); break; case OUTPUT_LVDS: or_config = (mode_ctrl & 0x00000f00) >> 8; @@ -895,12 +953,12 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); - push = evo_wait(encoder->dev, 0, 4); + push = evo_wait(dev, 0, 4); if (push) { 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); + evo_kick(push, dev, 0); } nv_encoder->crtc = encoder->crtc; @@ -925,6 +983,8 @@ nvd0_sor_disconnect(struct drm_encoder *encoder) evo_kick(push, dev, 0); } + nvd0_hdmi_disconnect(encoder); + nv_encoder->crtc = NULL; nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; } -- cgit v1.2.3 From 64d9cc04ec08d36c2b39f04d4994313d3901a85c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 19:51:20 +1000 Subject: drm/nvd0/disp: enable hdmi on sor if hdmi monitor present Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index ceb59802d694..a54f96751e1f 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -811,13 +811,57 @@ nvd0_audio_disconnect(struct drm_encoder *encoder) static void nvd0_hdmi_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); + struct nouveau_connector *nv_connector; + struct drm_device *dev = encoder->dev; + int head = nv_crtc->index * 0x800; + u32 rekey = 56; /* binary driver, and tegra constant */ + u32 max_ac_packet; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_hdmi_monitor(nv_connector->edid)) + return; + + max_ac_packet = mode->htotal - mode->hdisplay; + max_ac_packet -= rekey; + max_ac_packet -= 18; /* constant from tegra */ + max_ac_packet /= 32; + + /* AVI InfoFrame */ + nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000); + nv_wr32(dev, 0x61671c + head, 0x000d0282); + nv_wr32(dev, 0x616720 + head, 0x0000006f); + nv_wr32(dev, 0x616724 + head, 0x00000000); + nv_wr32(dev, 0x616728 + head, 0x00000000); + nv_wr32(dev, 0x61672c + head, 0x00000000); + nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000001); + + /* ??? InfoFrame? */ + nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000); + nv_wr32(dev, 0x6167ac + head, 0x00000010); + nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000001); + + /* HDMI_CTRL */ + nv_mask(dev, 0x616798 + head, 0x401f007f, 0x40000000 | rekey | + max_ac_packet << 16); + nvd0_audio_mode_set(encoder, mode); } static void nvd0_hdmi_disconnect(struct drm_encoder *encoder) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + struct drm_device *dev = encoder->dev; + int head = nv_crtc->index * 0x800; + nvd0_audio_disconnect(encoder); + + nv_mask(dev, 0x616798 + head, 0x40000000, 0x00000000); + nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000); + nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000); } /****************************************************************************** -- cgit v1.2.3 From 27517ddbdcc59ab43ed18d7bbe53680d428bbd20 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 20:26:44 +1000 Subject: drm/nvd0/disp: hook evo up to debugging Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index a54f96751e1f..5d01f1b64e26 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -84,6 +84,9 @@ evo_wait(struct drm_device *dev, int id, int nr) put = 0; } + if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) + NV_INFO(dev, "Evo%d: %p START\n", id, disp->evo[id].ptr + put); + return disp->evo[id].ptr + put; } @@ -91,6 +94,16 @@ static void evo_kick(u32 *push, struct drm_device *dev, int id) { struct nvd0_display *disp = nvd0_display(dev); + + if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) { + u32 curp = nv_rd32(dev, 0x640000 + (id * 0x1000)) >> 2; + u32 *cur = disp->evo[id].ptr + curp; + + while (cur < push) + NV_INFO(dev, "Evo%d: 0x%08x\n", id, *cur++); + NV_INFO(dev, "Evo%d: %p KICK!\n", id, push); + } + nv_wr32(dev, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2); } -- cgit v1.2.3 From 091e40cd9529968bacbbb18fc05d6dea65421a5b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 20:46:00 +1000 Subject: drm/nvd0/disp: enable hdmi audio Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 5d01f1b64e26..75f995852796 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -859,6 +859,9 @@ nvd0_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) nv_mask(dev, 0x616798 + head, 0x401f007f, 0x40000000 | rekey | max_ac_packet << 16); + /* NFI, audio doesn't work without it though.. */ + nv_mask(dev, 0x616548 + head, 0x00000070, 0x00000000); + nvd0_audio_mode_set(encoder, mode); } -- cgit v1.2.3 From d4676461a75699ec0f6d271e3fe75ee631755c6e Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Tue, 1 Nov 2011 11:38:16 +0100 Subject: drm/nv50/pm: fix a typo in clock calculation Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index e7ff5acce65f..e025cae5ef23 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -295,7 +295,7 @@ read_clk(struct drm_device *dev, enum clk_src src) case 0x00000800: return read_clk(dev, clk_src_hclkm3d2) >> P; case 0x00000c00: - return read_pll(dev, clk_src_mclk) >> P; + return read_clk(dev, clk_src_mclk) >> P; } break; } -- cgit v1.2.3 From d2edab4acffb35a6e24259886d377774efd37e6e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 10 Nov 2011 13:20:14 +1000 Subject: drm/nouveau/pm: fix missing volt changes when boot voltage is undefined Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index c6ebf693ffb8..1442d0673671 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -117,7 +117,7 @@ nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl, } if (pm->voltage.supported && pm->voltage_set) { - if (a->volt_min && b->volt_min && b->volt_min > a->volt_min) { + if (perflvl->volt_min && b->volt_min > a->volt_min) { ret = pm->voltage_set(dev, perflvl->volt_min); if (ret) { NV_ERROR(dev, "voltage set failed: %d\n", ret); -- cgit v1.2.3 From 2a44e4997c5fee8e1da1589ff57e0bd1c53f03ce Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 9 Nov 2011 11:36:33 +1000 Subject: drm/nouveau/disp: introduce proper init/fini, separate from create/destroy Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 6 +++++- drivers/gpu/drm/nouveau/nouveau_drv.h | 9 ++++++--- drivers/gpu/drm/nouveau/nouveau_state.c | 24 ++++++++++++++++-------- drivers/gpu/drm/nouveau/nv04_display.c | 5 +++++ drivers/gpu/drm/nouveau/nv50_display.c | 17 +++++------------ drivers/gpu/drm/nouveau/nv50_display.h | 1 + drivers/gpu/drm/nouveau/nvd0_display.c | 8 +------- 7 files changed, 39 insertions(+), 31 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 38623a25d0b1..803248d467fb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -262,7 +262,10 @@ nouveau_display_create(struct drm_device *dev) if (ret) return ret; - return 0; + ret = disp->init(dev); + if (ret) + disp->destroy(dev); + return ret; } void @@ -271,6 +274,7 @@ nouveau_display_destroy(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_display_engine *disp = &dev_priv->engine.display; + disp->fini(dev); disp->destroy(dev); drm_mode_config_cleanup(dev); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index dc88248f2305..a22ca4735943 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -396,8 +396,9 @@ struct nouveau_display_engine { int (*early_init)(struct drm_device *); void (*late_takedown)(struct drm_device *); int (*create)(struct drm_device *); - int (*init)(struct drm_device *); void (*destroy)(struct drm_device *); + int (*init)(struct drm_device *); + void (*fini)(struct drm_device *); struct drm_property *dithering_mode; struct drm_property *dithering_depth; @@ -1343,13 +1344,15 @@ extern int nv17_tv_create(struct drm_connector *, struct dcb_entry *); extern int nv04_display_early_init(struct drm_device *); extern void nv04_display_late_takedown(struct drm_device *); extern int nv04_display_create(struct drm_device *); -extern int nv04_display_init(struct drm_device *); extern void nv04_display_destroy(struct drm_device *); +extern int nv04_display_init(struct drm_device *); +extern void nv04_display_fini(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 *); +extern int nvd0_display_init(struct drm_device *); +extern void nvd0_display_fini(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 46831fea7861..0c3368b36e6c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -80,8 +80,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; engine->gpio.init = nouveau_stub_init; engine->gpio.takedown = nouveau_stub_takedown; engine->gpio.get = NULL; @@ -129,8 +130,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; engine->gpio.init = nouveau_stub_init; engine->gpio.takedown = nouveau_stub_takedown; engine->gpio.get = nv10_gpio_get; @@ -178,8 +180,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; engine->gpio.init = nouveau_stub_init; engine->gpio.takedown = nouveau_stub_takedown; engine->gpio.get = nv10_gpio_get; @@ -227,8 +230,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; engine->gpio.init = nouveau_stub_init; engine->gpio.takedown = nouveau_stub_takedown; engine->gpio.get = nv10_gpio_get; @@ -279,8 +283,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; - engine->display.init = nv04_display_init; engine->display.destroy = nv04_display_destroy; + engine->display.init = nv04_display_init; + engine->display.fini = nv04_display_fini; engine->gpio.init = nouveau_stub_init; engine->gpio.takedown = nouveau_stub_takedown; engine->gpio.get = nv10_gpio_get; @@ -336,8 +341,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; - engine->display.init = nv50_display_init; engine->display.destroy = nv50_display_destroy; + engine->display.init = nv50_display_init; + engine->display.fini = nv50_display_fini; engine->gpio.init = nv50_gpio_init; engine->gpio.takedown = nv50_gpio_fini; engine->gpio.get = nv50_gpio_get; @@ -411,8 +417,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; - engine->display.init = nv50_display_init; engine->display.destroy = nv50_display_destroy; + engine->display.init = nv50_display_init; + engine->display.fini = nv50_display_fini; engine->gpio.init = nv50_gpio_init; engine->gpio.takedown = nouveau_stub_takedown; engine->gpio.get = nv50_gpio_get; @@ -463,8 +470,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.early_init = nouveau_stub_init; engine->display.late_takedown = nouveau_stub_takedown; engine->display.create = nvd0_display_create; - engine->display.init = nvd0_display_init; engine->display.destroy = nvd0_display_destroy; + engine->display.init = nvd0_display_init; + engine->display.fini = nvd0_display_fini; 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/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 6bd8518d7b2e..7047d37e8dab 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -243,6 +243,11 @@ nv04_display_init(struct drm_device *dev) return 0; } +void +nv04_display_fini(struct drm_device *dev) +{ +} + static void nv04_vblank_crtc0_isr(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index a59d0b923cc8..7a57c30e2728 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -264,7 +264,8 @@ nv50_display_init(struct drm_device *dev) return nv50_display_sync(dev); } -static int nv50_display_disable(struct drm_device *dev) +void +nv50_display_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_display *disp = nv50_display(dev); @@ -337,16 +338,16 @@ static int nv50_display_disable(struct drm_device *dev) nv_wr32(dev, 0xe074, 0xffffffff); nv_wr32(dev, 0xe070, 0x00000000); } - return 0; } -int nv50_display_create(struct drm_device *dev) +int +nv50_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct dcb_table *dcb = &dev_priv->vbios.dcb; struct drm_connector *connector, *ct; struct nv50_display *priv; - int ret, i; + int i; NV_DEBUG_KMS(dev, "\n"); @@ -399,13 +400,6 @@ int nv50_display_create(struct drm_device *dev) tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); nouveau_irq_register(dev, 26, nv50_display_isr); - - ret = nv50_display_init(dev); - if (ret) { - nv50_display_destroy(dev); - return ret; - } - return 0; } @@ -416,7 +410,6 @@ nv50_display_destroy(struct drm_device *dev) NV_DEBUG_KMS(dev, "\n"); - nv50_display_disable(dev); nouveau_irq_unregister(dev, 26); kfree(disp); } diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index 0cd8819a53f6..11e2f1efd411 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -69,6 +69,7 @@ int nv50_display_early_init(struct drm_device *dev); void nv50_display_late_takedown(struct drm_device *dev); int nv50_display_create(struct drm_device *dev); int nv50_display_init(struct drm_device *dev); +void nv50_display_fini(struct drm_device *dev); void nv50_display_destroy(struct drm_device *dev); int nv50_crtc_blank(struct nouveau_crtc *, bool blank); int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 75f995852796..614c6520a520 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -1341,7 +1341,7 @@ nvd0_display_intr(struct drm_device *dev) /****************************************************************************** * Init *****************************************************************************/ -static void +void nvd0_display_fini(struct drm_device *dev) { int i; @@ -1461,8 +1461,6 @@ nvd0_display_destroy(struct drm_device *dev) struct nvd0_display *disp = nvd0_display(dev); struct pci_dev *pdev = dev->pdev; - nvd0_display_fini(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); @@ -1588,10 +1586,6 @@ nvd0_display_create(struct drm_device *dev) goto out; } - ret = nvd0_display_init(dev); - if (ret) - goto out; - out: if (ret) nvd0_display_destroy(dev); -- cgit v1.2.3 From cf41d53bf5b95d77673b185cc3b20ae3257f79e2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 9 Nov 2011 14:31:16 +1000 Subject: drm/nouveau: re-jig fbcon suspend/resume process a little Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 22 ++++++---------------- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 7 +++++++ 2 files changed, 13 insertions(+), 16 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index ffde896b1f3c..cb357ab3670c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -180,8 +180,8 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) drm_kms_helper_poll_disable(dev); - NV_INFO(dev, "Disabling fbcon acceleration...\n"); - nouveau_fbcon_save_disable_accel(dev); + NV_INFO(dev, "Disabling fbcon...\n"); + nouveau_fbcon_set_suspend(dev, 1); NV_INFO(dev, "Unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -248,10 +248,6 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) pci_set_power_state(pdev, PCI_D3hot); } - console_lock(); - nouveau_fbcon_set_suspend(dev, 1); - console_unlock(); - nouveau_fbcon_restore_accel(dev); return 0; out_abort: @@ -277,8 +273,6 @@ nouveau_pci_resume(struct pci_dev *pdev) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - nouveau_fbcon_save_disable_accel(dev); - NV_INFO(dev, "We're back, enabling device...\n"); pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); @@ -360,7 +354,11 @@ nouveau_pci_resume(struct pci_dev *pdev) NV_ERROR(dev, "Could not pin/map cursor.\n"); } + nouveau_fbcon_set_suspend(dev, 0); + nouveau_fbcon_zfill_all(dev); + engine->display.init(dev); + drm_kms_helper_poll_enable(dev); /* Force CLUT to get re-loaded during modeset */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -369,12 +367,6 @@ nouveau_pci_resume(struct pci_dev *pdev) nv_crtc->lut.depth = 0; } - console_lock(); - nouveau_fbcon_set_suspend(dev, 0); - console_unlock(); - - nouveau_fbcon_zfill_all(dev); - drm_helper_resume_force_mode(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -386,8 +378,6 @@ nouveau_pci_resume(struct pci_dev *pdev) nv_crtc->cursor_saved_y); } - nouveau_fbcon_restore_accel(dev); - drm_kms_helper_poll_enable(dev); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index dbb151834121..9892218d7452 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "drmP.h" #include "drm.h" @@ -548,7 +549,13 @@ void nouveau_fbcon_restore_accel(struct drm_device *dev) void nouveau_fbcon_set_suspend(struct drm_device *dev, int state) { struct drm_nouveau_private *dev_priv = dev->dev_private; + console_lock(); + if (state == 0) + nouveau_fbcon_save_disable_accel(dev); fb_set_suspend(dev_priv->nfbdev->helper.fbdev, state); + if (state == 1) + nouveau_fbcon_restore_accel(dev); + console_unlock(); } void nouveau_fbcon_zfill_all(struct drm_device *dev) -- cgit v1.2.3 From 1772fcc6f8af681a6e08f5cca15e86e710efe809 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 9 Nov 2011 15:52:43 +1000 Subject: drm/nv50/disp: fix evo for create/init + destroy/fini split Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_display.c | 10 +++++++++- drivers/gpu/drm/nouveau/nv50_display.h | 2 ++ drivers/gpu/drm/nouveau/nv50_evo.c | 12 ++---------- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 7a57c30e2728..a3d28a1e8bb2 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -347,7 +347,7 @@ nv50_display_create(struct drm_device *dev) struct dcb_table *dcb = &dev_priv->vbios.dcb; struct drm_connector *connector, *ct; struct nv50_display *priv; - int i; + int ret, i; NV_DEBUG_KMS(dev, "\n"); @@ -400,6 +400,13 @@ nv50_display_create(struct drm_device *dev) tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); nouveau_irq_register(dev, 26, nv50_display_isr); + + ret = nv50_evo_create(dev); + if (ret) { + nv50_display_destroy(dev); + return ret; + } + return 0; } @@ -410,6 +417,7 @@ nv50_display_destroy(struct drm_device *dev) NV_DEBUG_KMS(dev, "\n"); + nv50_evo_destroy(dev); nouveau_irq_unregister(dev, 26); kfree(disp); } diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index 11e2f1efd411..95874f7c043c 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -79,6 +79,8 @@ int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, struct nouveau_channel *chan); void nv50_display_flip_stop(struct drm_crtc *); +int nv50_evo_create(struct drm_device *dev); +void nv50_evo_destroy(struct drm_device *dev); int nv50_evo_init(struct drm_device *dev); void nv50_evo_fini(struct drm_device *dev); void nv50_evo_dmaobj_init(struct nouveau_gpuobj *, u32 memtype, u64 base, diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c index c99d9751880c..9b962e989d7c 100644 --- a/drivers/gpu/drm/nouveau/nv50_evo.c +++ b/drivers/gpu/drm/nouveau/nv50_evo.c @@ -218,7 +218,7 @@ nv50_evo_channel_fini(struct nouveau_channel *evo) } } -static void +void nv50_evo_destroy(struct drm_device *dev) { struct nv50_display *disp = nv50_display(dev); @@ -235,7 +235,7 @@ nv50_evo_destroy(struct drm_device *dev) nv50_evo_channel_del(&disp->master); } -static int +int nv50_evo_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -388,12 +388,6 @@ nv50_evo_init(struct drm_device *dev) struct nv50_display *disp = nv50_display(dev); int ret, i; - if (!disp->master) { - ret = nv50_evo_create(dev); - if (ret) - return ret; - } - ret = nv50_evo_channel_init(disp->master); if (ret) return ret; @@ -420,6 +414,4 @@ nv50_evo_fini(struct drm_device *dev) if (disp->master) nv50_evo_channel_fini(disp->master); - - nv50_evo_destroy(dev); } -- cgit v1.2.3 From f62b27db6b5479efe376b408802a081a834ef50e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 9 Nov 2011 15:18:47 +1000 Subject: drm/nouveau: shutdown display on suspend/hibernate Known to fix some serious issues with hibernate on a couple of systems. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 42 ++++++++++++++++++++++++++++--- drivers/gpu/drm/nouveau/nouveau_drv.c | 6 ++--- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 6 ++--- 4 files changed, 45 insertions(+), 11 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 803248d467fb..6ac6931624f4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -206,6 +206,31 @@ static struct drm_prop_enum_list dither_depth[] = { } \ } while(0) +int +nouveau_display_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + int ret; + + ret = disp->init(dev); + if (ret == 0) { + drm_kms_helper_poll_enable(dev); + } + + return ret; +} + +void +nouveau_display_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_display_engine *disp = &dev_priv->engine.display; + + drm_kms_helper_poll_disable(dev); + disp->fini(dev); +} + int nouveau_display_create(struct drm_device *dev) { @@ -258,13 +283,19 @@ nouveau_display_create(struct drm_device *dev) dev->mode_config.max_height = 8192; } + drm_kms_helper_poll_init(dev); + drm_kms_helper_poll_disable(dev); + ret = disp->create(dev); if (ret) return ret; - ret = disp->init(dev); - if (ret) - disp->destroy(dev); + if (dev->mode_config.num_crtc) { + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) + return ret; + } + return ret; } @@ -274,8 +305,11 @@ nouveau_display_destroy(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_display_engine *disp = &dev_priv->engine.display; - disp->fini(dev); + drm_vblank_cleanup(dev); + disp->destroy(dev); + + drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index cb357ab3670c..e4485404892e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -178,7 +178,8 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - drm_kms_helper_poll_disable(dev); + NV_INFO(dev, "Disabling display...\n"); + nouveau_display_fini(dev); NV_INFO(dev, "Disabling fbcon...\n"); nouveau_fbcon_set_suspend(dev, 1); @@ -357,8 +358,7 @@ nouveau_pci_resume(struct pci_dev *pdev) nouveau_fbcon_set_suspend(dev, 0); nouveau_fbcon_zfill_all(dev); - engine->display.init(dev); - drm_kms_helper_poll_enable(dev); + nouveau_display_init(dev); /* Force CLUT to get re-loaded during modeset */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index a22ca4735943..c13918588034 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1449,6 +1449,8 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *, /* nouveau_display.c */ int nouveau_display_create(struct drm_device *dev); void nouveau_display_destroy(struct drm_device *dev); +int nouveau_display_init(struct drm_device *dev); +void nouveau_display_fini(struct drm_device *dev); int nouveau_vblank_enable(struct drm_device *dev, int crtc); void nouveau_vblank_disable(struct drm_device *dev, int crtc); int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 0c3368b36e6c..013b33bac302 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -763,12 +763,11 @@ nouveau_card_init(struct drm_device *dev) } if (dev->mode_config.num_crtc) { - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + ret = nouveau_display_init(dev); if (ret) goto out_chan; nouveau_fbcon_init(dev); - drm_kms_helper_poll_init(dev); } return 0; @@ -829,9 +828,8 @@ static void nouveau_card_takedown(struct drm_device *dev) int e; if (dev->mode_config.num_crtc) { - drm_kms_helper_poll_fini(dev); nouveau_fbcon_fini(dev); - drm_vblank_cleanup(dev); + nouveau_display_fini(dev); } if (dev_priv->channel) { -- cgit v1.2.3 From 2d1d898b4684ab86fb27ece7d69e4e145a7be9d2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 23:39:22 +1000 Subject: drm/nvd0/disp: update crtc timing calculations for interlace/doublescan Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 52 ++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 614c6520a520..7c2defdcc8f3 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -352,21 +352,35 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, { 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 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; + u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; + u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; + u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; + u32 vblan2e = 0, vblan2s = 1; + u32 magic = 0x31ec6000; u32 syncs, *push; int ret; + hactive = mode->htotal; + hsynce = mode->hsync_end - mode->hsync_start - 1; + hbackp = mode->htotal - mode->hsync_end; + hblanke = hsynce + hbackp; + hfrontp = mode->hsync_start - mode->hdisplay; + hblanks = mode->htotal - hfrontp - 1; + + vactive = mode->vtotal * vscan / ilace; + vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; + vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; + vblanke = vsynce + vbackp; + vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; + vblanks = vactive - vfrontp - 1; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vblan2e = vactive + vsynce + vbackp; + vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); + vactive = (vactive * 2) + 1; + magic |= 0x00000001; + } + syncs = 0x00000001; if (mode->flags & DRM_MODE_FLAG_NHSYNC) syncs |= 0x00000008; @@ -379,20 +393,22 @@ 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_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6); evo_data(push, 0x00000000); - 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_data(push, (vactive << 16) | hactive); + evo_data(push, ( vsynce << 16) | hsynce); + evo_data(push, (vblanke << 16) | hblanke); + evo_data(push, (vblanks << 16) | hblanks); + evo_data(push, (vblan2e << 16) | vblan2s); 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, 0x0404 + (nv_crtc->index * 0x300), 1); + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); evo_data(push, syncs); + evo_data(push, magic); evo_kick(push, crtc->dev, 0); } -- cgit v1.2.3 From 9285462273cbccb27187d5308ed95f94a9ceb1de Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 23:49:06 +1000 Subject: drm/nvd0/disp: scaler updates, overscan compensation etc Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 6 +- drivers/gpu/drm/nouveau/nvd0_display.c | 94 +++++++++++++++++++++---------- 2 files changed, 67 insertions(+), 33 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 6ac6931624f4..fdeb64a3a84d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -161,9 +161,9 @@ struct drm_prop_enum_list { }; static struct drm_prop_enum_list underscan[] = { - { 2, UNDERSCAN_AUTO, "auto" }, - { 2, UNDERSCAN_OFF, "off" }, - { 2, UNDERSCAN_ON, "on" }, + { 6, UNDERSCAN_AUTO, "auto" }, + { 6, UNDERSCAN_OFF, "off" }, + { 6, UNDERSCAN_ON, "on" }, {} }; diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 7c2defdcc8f3..dcfe6192e5c4 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -160,49 +160,83 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) static int nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) { - struct drm_display_mode *mode = &nv_crtc->base.mode; + struct drm_display_mode *omode, *umode = &nv_crtc->base.mode; struct drm_device *dev = nv_crtc->base.dev; struct nouveau_connector *nv_connector; - u32 *push, outX, outY; - - outX = mode->hdisplay; - outY = mode->vdisplay; + int mode = DRM_MODE_SCALE_NONE; + u32 oX, oY, *push; + /* start off at the resolution we programmed the crtc for, this + * effectively handles NONE/FULL scaling + */ 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 (nv_connector->scaling_mode) { - 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; + if (nv_connector && nv_connector->native_mode) + mode = nv_connector->scaling_mode; + + if (mode != DRM_MODE_SCALE_NONE) + omode = nv_connector->native_mode; + else + omode = umode; + + oX = omode->hdisplay; + oY = omode->vdisplay; + if (omode->flags & DRM_MODE_FLAG_DBLSCAN) + oY *= 2; + + /* add overscan compensation if necessary, will keep the aspect + * ratio the same as the backend mode unless overridden by the + * user setting both hborder and vborder properties. + */ + if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || + (nv_connector->underscan == UNDERSCAN_AUTO && + nv_connector->edid && + drm_detect_hdmi_monitor(nv_connector->edid)))) { + u32 bX = nv_connector->underscan_hborder; + u32 bY = nv_connector->underscan_vborder; + u32 aspect = (oY << 19) / oX; + + if (bX) { + oX -= (bX * 2); + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; + } else { + oX -= (oX >> 4) + 32; + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; } } + /* handle CENTER/ASPECT scaling, taking into account the areas + * removed already for overscan compensation + */ + switch (mode) { + case DRM_MODE_SCALE_CENTER: + oX = min((u32)umode->hdisplay, oX); + oY = min((u32)umode->vdisplay, oY); + /* fall-through */ + case DRM_MODE_SCALE_ASPECT: + if (oY < oX) { + u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; + oX = ((oY * aspect) + (aspect / 2)) >> 19; + } else { + u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; + oY = ((oX * aspect) + (aspect / 2)) >> 19; + } + break; + default: + break; + } + push = evo_wait(dev, 0, 16); if (push) { evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); - evo_data(push, (outY << 16) | outX); - evo_data(push, (outY << 16) | outX); - evo_data(push, (outY << 16) | outX); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); evo_mthd(push, 0x0494 + (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); + evo_data(push, (umode->vdisplay << 16) | umode->hdisplay); if (update) { evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); -- cgit v1.2.3 From bdb8c212bec7410dadcf7c406d43f5b9219f0e56 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 12 Nov 2011 01:30:24 +1000 Subject: drm/nvd0/disp: init display sync channels Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 170 ++++++++++++++++++++++----------- 1 file changed, 115 insertions(+), 55 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index dcfe6192e5c4..649a4a6e9227 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -35,12 +35,16 @@ #include "nouveau_fb.h" #include "nv50_display.h" +#define EVO_MASTER (0x00) +#define EVO_SYNC(c) (0x01 + (c)) +#define EVO_CURS(c) (0x0d + (c)) + struct nvd0_display { struct nouveau_gpuobj *mem; struct { dma_addr_t handle; u32 *ptr; - } evo[1]; + } evo[3]; struct tasklet_struct tasklet; u32 modeset; @@ -53,6 +57,15 @@ nvd0_display(struct drm_device *dev) return dev_priv->engine.display.priv; } +static struct drm_crtc * +nvd0_display_crtc_get(struct drm_encoder *encoder) +{ + return nouveau_encoder(encoder)->crtc; +} + +/****************************************************************************** + * EVO channel helpers + *****************************************************************************/ static inline int evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data) { @@ -110,10 +123,72 @@ 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) +static int +evo_init_dma(struct drm_device *dev, int ch) { - return nouveau_encoder(encoder)->crtc; + struct nvd0_display *disp = nvd0_display(dev); + u32 flags; + + flags = 0x00000000; + if (ch == EVO_MASTER) + flags |= 0x01000000; + + nv_wr32(dev, 0x610494 + (ch * 0x0010), (disp->evo[ch].handle >> 8) | 3); + nv_wr32(dev, 0x610498 + (ch * 0x0010), 0x00010000); + nv_wr32(dev, 0x61049c + (ch * 0x0010), 0x00000001); + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010); + nv_wr32(dev, 0x640000 + (ch * 0x1000), 0x00000000); + nv_wr32(dev, 0x610490 + (ch * 0x0010), 0x00000013 | flags); + if (!nv_wait(dev, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000)) { + NV_ERROR(dev, "PDISP: ch%d 0x%08x\n", ch, + nv_rd32(dev, 0x610490 + (ch * 0x0010))); + return -EBUSY; + } + + nv_mask(dev, 0x610090, (1 << ch), (1 << ch)); + nv_mask(dev, 0x6100a0, (1 << ch), (1 << ch)); + return 0; +} + +static void +evo_fini_dma(struct drm_device *dev, int ch) +{ + if (!(nv_rd32(dev, 0x610490 + (ch * 0x0010)) & 0x00000010)) + return; + + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000000); + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000003, 0x00000000); + nv_wait(dev, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000); + nv_mask(dev, 0x610090, (1 << ch), 0x00000000); + nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000); +} + +static int +evo_init_pio(struct drm_device *dev, int ch) +{ + nv_wr32(dev, 0x610490 + (ch * 0x0010), 0x00000001); + if (!nv_wait(dev, 0x610490 + (ch * 0x0010), 0x00010000, 0x00010000)) { + NV_ERROR(dev, "PDISP: ch%d 0x%08x\n", ch, + nv_rd32(dev, 0x610490 + (ch * 0x0010))); + return -EBUSY; + } + + nv_mask(dev, 0x610090, (1 << ch), (1 << ch)); + nv_mask(dev, 0x6100a0, (1 << ch), (1 << ch)); + return 0; +} + +static void +evo_fini_pio(struct drm_device *dev, int ch) +{ + if (!(nv_rd32(dev, 0x610490 + (ch * 0x0010)) & 0x00000001)) + return; + + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010); + nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000001, 0x00000000); + nv_wait(dev, 0x610490 + (ch * 0x0010), 0x00010000, 0x00000000); + nv_mask(dev, 0x610090, (1 << ch), 0x00000000); + nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000); } /****************************************************************************** @@ -1396,33 +1471,22 @@ 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 cursors + syncs */ + for (i = 1; i >= 0; i--) { + evo_fini_pio(dev, EVO_CURS(i)); + evo_fini_dma(dev, EVO_SYNC(i)); } /* 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); - } + evo_fini_dma(dev, EVO_MASTER); } int nvd0_display_init(struct drm_device *dev) { struct nvd0_display *disp = nvd0_display(dev); + int ret, i; u32 *push; - int i; if (nv_rd32(dev, 0x6100ac) & 0x00000100) { nv_wr32(dev, 0x6100ac, 0x00000100); @@ -1447,7 +1511,7 @@ nvd0_display_init(struct drm_device *dev) nv_wr32(dev, 0x6301c4 + (i * 0x800), sor); } - for (i = 0; i < 2; i++) { + for (i = 0; i < dev->mode_config.num_crtc; 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)); @@ -1461,36 +1525,22 @@ nvd0_display_init(struct drm_device *dev) nv_mask(dev, 0x6100b0, 0x00000307, 0x00000307); /* init master */ - nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3); - nv_wr32(dev, 0x610498, 0x00010000); - nv_wr32(dev, 0x61049c, 0x00000001); - 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; - } + ret = evo_init_dma(dev, EVO_MASTER); + if (ret) + goto error; - nv_mask(dev, 0x610090, 1 << i, 1 << i); - nv_mask(dev, 0x6100a0, 1 << i, 1 << i); + /* init syncs + cursors */ + for (i = 0; i < dev->mode_config.num_crtc; i++) { + if ((ret = evo_init_dma(dev, EVO_SYNC(i))) || + (ret = evo_init_pio(dev, EVO_CURS(i)))) + goto error; } push = evo_wait(dev, 0, 32); - if (!push) - return -EBUSY; + if (!push) { + ret = -EBUSY; + goto error; + } evo_mthd(push, 0x0088, 1); evo_data(push, NvEvoSync); evo_mthd(push, 0x0084, 1); @@ -1501,7 +1551,10 @@ nvd0_display_init(struct drm_device *dev) evo_data(push, 0x00000000); evo_kick(push, dev, 0); - return 0; +error: + if (ret) + nvd0_display_fini(dev); + return ret; } void @@ -1510,8 +1563,13 @@ 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; + int i; + + for (i = 0; i < 3; i++) { + pci_free_consistent(pdev, PAGE_SIZE, disp->evo[i].ptr, + disp->evo[i].handle); + } - 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); @@ -1629,11 +1687,13 @@ nvd0_display_create(struct drm_device *dev) pinstmem->flush(dev); /* 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; + for (i = 0; i < 3; i++) { + disp->evo[i].ptr = pci_alloc_consistent(pdev, PAGE_SIZE, + &disp->evo[i].handle); + if (!disp->evo[i].ptr) { + ret = -ENOMEM; + goto out; + } } out: -- cgit v1.2.3 From 2eac77b793196b97a35f9c5579411eb0aa085047 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 12 Nov 2011 12:53:36 +1000 Subject: drm/nvd0/disp: have evo names now, use them Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 649a4a6e9227..3179f2a9ed23 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -218,7 +218,7 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) mode |= nv_connector->dithering_depth; } - push = evo_wait(dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0490 + (nv_crtc->index * 0x300), 1); evo_data(push, mode); @@ -226,7 +226,7 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); } - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } return 0; @@ -302,7 +302,7 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) break; } - push = evo_wait(dev, 0, 16); + push = evo_wait(dev, EVO_MASTER, 16); if (push) { evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); evo_data(push, (oY << 16) | oX); @@ -316,7 +316,7 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); } - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } return 0; @@ -329,7 +329,7 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb); u32 *push; - push = evo_wait(fb->dev, 0, 16); + push = evo_wait(fb->dev, EVO_MASTER, 16); if (push) { evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1); evo_data(push, nvfb->nvbo->bo.offset >> 8); @@ -344,7 +344,7 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); } - evo_kick(push, fb->dev, 0); + evo_kick(push, fb->dev, EVO_MASTER); } nv_crtc->fb.tile_flags = nvfb->r_dma; @@ -355,7 +355,7 @@ 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); + u32 *push = evo_wait(dev, EVO_MASTER, 16); if (push) { if (show) { evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2); @@ -375,7 +375,7 @@ nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update) evo_data(push, 0x00000000); } - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } } @@ -390,7 +390,7 @@ nvd0_crtc_prepare(struct drm_crtc *crtc) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); u32 *push; - push = evo_wait(crtc->dev, 0, 2); + push = evo_wait(crtc->dev, EVO_MASTER, 2); if (push) { evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); @@ -398,7 +398,7 @@ nvd0_crtc_prepare(struct drm_crtc *crtc) evo_data(push, 0x03000000); evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); - evo_kick(push, crtc->dev, 0); + evo_kick(push, crtc->dev, EVO_MASTER); } nvd0_crtc_cursor_show(nv_crtc, false, false); @@ -410,7 +410,7 @@ nvd0_crtc_commit(struct drm_crtc *crtc) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); u32 *push; - push = evo_wait(crtc->dev, 0, 32); + push = evo_wait(crtc->dev, EVO_MASTER, 32); if (push) { evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); evo_data(push, nv_crtc->fb.tile_flags); @@ -423,7 +423,7 @@ nvd0_crtc_commit(struct drm_crtc *crtc) evo_data(push, NvEvoVRAM); evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1); evo_data(push, 0xffffff00); - evo_kick(push, crtc->dev, 0); + evo_kick(push, crtc->dev, EVO_MASTER); } nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true); @@ -500,7 +500,7 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, if (ret) return ret; - push = evo_wait(crtc->dev, 0, 64); + push = evo_wait(crtc->dev, EVO_MASTER, 64); if (push) { evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6); evo_data(push, 0x00000000); @@ -518,7 +518,7 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); evo_data(push, syncs); evo_data(push, magic); - evo_kick(push, crtc->dev, 0); + evo_kick(push, crtc->dev, EVO_MASTER); } nv_connector = nouveau_crtc_connector_get(nv_crtc); @@ -803,12 +803,12 @@ 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, 4); + push = evo_wait(encoder->dev, EVO_MASTER, 4); if (push) { 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); + evo_kick(push, encoder->dev, EVO_MASTER); } nv_encoder->crtc = encoder->crtc; @@ -824,13 +824,13 @@ nvd0_dac_disconnect(struct drm_encoder *encoder) if (nv_encoder->crtc) { nvd0_crtc_prepare(nv_encoder->crtc); - push = evo_wait(dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 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); + evo_kick(push, dev, EVO_MASTER); } nv_encoder->crtc = NULL; @@ -1138,12 +1138,12 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); - push = evo_wait(dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2); evo_data(push, mode_ctrl); evo_data(push, or_config); - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } nv_encoder->crtc = encoder->crtc; @@ -1159,13 +1159,13 @@ nvd0_sor_disconnect(struct drm_encoder *encoder) if (nv_encoder->crtc) { nvd0_crtc_prepare(nv_encoder->crtc); - push = evo_wait(dev, 0, 4); + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); evo_data(push, 0x00000000); evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); } nvd0_hdmi_disconnect(encoder); @@ -1536,7 +1536,7 @@ nvd0_display_init(struct drm_device *dev) goto error; } - push = evo_wait(dev, 0, 32); + push = evo_wait(dev, EVO_MASTER, 32); if (!push) { ret = -EBUSY; goto error; @@ -1549,7 +1549,7 @@ nvd0_display_init(struct drm_device *dev) evo_data(push, 0x80000000); evo_mthd(push, 0x008c, 1); evo_data(push, 0x00000000); - evo_kick(push, dev, 0); + evo_kick(push, dev, EVO_MASTER); error: if (ret) -- cgit v1.2.3 From 4acd42939867df4596ae3b433ec1f80e0c461038 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 12 Nov 2011 12:57:54 +1000 Subject: drm/nvd0/disp: make it clearer that the cursor regs are pio evo channels Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 3179f2a9ed23..0c788ebe09ab 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -163,6 +163,12 @@ evo_fini_dma(struct drm_device *dev, int ch) nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000); } +static inline void +evo_piow(struct drm_device *dev, int ch, u16 mthd, u32 data) +{ + nv_wr32(dev, 0x640000 + (ch * 0x1000) + mthd, data); +} + static int evo_init_pio(struct drm_device *dev, int ch) { @@ -616,10 +622,10 @@ 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; + int ch = EVO_CURS(nv_crtc->index); - nv_wr32(crtc->dev, 0x64d084 + (nv_crtc->index * 0x1000), data); - nv_wr32(crtc->dev, 0x64d080 + (nv_crtc->index * 0x1000), 0x00000000); + evo_piow(crtc->dev, ch, 0x0084, (y << 16) | x); + evo_piow(crtc->dev, ch, 0x0080, 0x00000000); return 0; } -- cgit v1.2.3 From 3376ee374d2318d311bd3aa7b9bb0186f64ccca5 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 12 Nov 2011 14:28:12 +1000 Subject: drm/nvd0/disp: add support for page flipping Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 5 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 4 + drivers/gpu/drm/nouveau/nouveau_object.c | 25 ++- drivers/gpu/drm/nouveau/nouveau_state.c | 2 +- drivers/gpu/drm/nouveau/nvd0_display.c | 296 ++++++++++++++++++++++++------ 5 files changed, 267 insertions(+), 65 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index fdeb64a3a84d..b186174fa72f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -466,7 +466,10 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Emit a page flip */ if (dev_priv->card_type >= NV_50) { - ret = nv50_display_flip_next(crtc, fb, chan); + if (dev_priv->card_type >= NV_D0) + ret = nvd0_display_flip_next(crtc, fb, chan, 0); + else + ret = nv50_display_flip_next(crtc, fb, chan); if (ret) { nouveau_channel_put(&chan); goto fail_unreserve; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index c13918588034..1e70005d8220 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1353,6 +1353,10 @@ extern int nvd0_display_create(struct drm_device *); extern void nvd0_display_destroy(struct drm_device *); extern int nvd0_display_init(struct drm_device *); extern void nvd0_display_fini(struct drm_device *); +struct nouveau_bo *nvd0_display_crtc_sema(struct drm_device *, int crtc); +void nvd0_display_flip_stop(struct drm_crtc *); +int nvd0_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, + struct nouveau_channel *, u32 swap_interval); /* nv04_crtc.c */ extern int nv04_crtc_create(struct drm_device *, int index); diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 960c0ae0c0c3..cc419fae794b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -723,14 +723,14 @@ 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]; - - ret = nouveau_bo_vma_add(dispc->sem.bo, chan->vm, - &chan->dispc_vma[i]); + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo; + if (dev_priv->card_type >= NV_D0) + bo = nvd0_display_crtc_sema(dev, i); + else + bo = nv50_display(dev)->crtc[i].sem.bo; + + ret = nouveau_bo_vma_add(bo, chan->vm, &chan->dispc_vma[i]); if (ret) return ret; } @@ -879,9 +879,14 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) NV_DEBUG(dev, "ch%d\n", chan->id); - if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) { + if (dev_priv->card_type >= NV_D0) { + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i); + nouveau_bo_vma_del(bo, &chan->dispc_vma[i]); + } + } else + if (dev_priv->card_type >= NV_50) { 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]); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 013b33bac302..5d8ad4ec3ac1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1244,7 +1244,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, getparam->value = 1; break; case NOUVEAU_GETPARAM_HAS_PAGEFLIP: - getparam->value = dev_priv->card_type < NV_D0; + getparam->value = 1; break; case NOUVEAU_GETPARAM_GRAPH_UNITS: /* NV40 and NV50 versions are quite different, but register diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 0c788ebe09ab..dec1dd844d31 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -39,12 +39,20 @@ #define EVO_SYNC(c) (0x01 + (c)) #define EVO_CURS(c) (0x0d + (c)) +struct evo { + int idx; + dma_addr_t handle; + u32 *ptr; + struct { + struct nouveau_bo *bo; + u32 offset; + u16 value; + } sem; +}; + struct nvd0_display { struct nouveau_gpuobj *mem; - struct { - dma_addr_t handle; - u32 *ptr; - } evo[3]; + struct evo evo[3]; struct tasklet_struct tasklet; u32 modeset; @@ -197,6 +205,152 @@ evo_fini_pio(struct drm_device *dev, int ch) nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000); } +static bool +evo_sync_wait(void *data) +{ + return nouveau_bo_rd32(data, 0) != 0x00000000; +} + +static int +evo_sync(struct drm_device *dev, int ch) +{ + struct nvd0_display *disp = nvd0_display(dev); + struct evo *evo = &disp->evo[ch]; + u32 *push; + + nouveau_bo_wr32(evo->sem.bo, 0, 0x00000000); + + push = evo_wait(dev, ch, 8); + if (push) { + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x80000000); + evo_mthd(push, 0x0080, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_kick(push, dev, ch); + if (nv_wait_cb(dev, evo_sync_wait, evo->sem.bo)) + return 0; + } + + return -EBUSY; +} + +/****************************************************************************** + * Sync channel (aka. page flipping) + *****************************************************************************/ +struct nouveau_bo * +nvd0_display_crtc_sema(struct drm_device *dev, int crtc) +{ + struct nvd0_display *disp = nvd0_display(dev); + struct evo *evo = &disp->evo[EVO_SYNC(crtc)]; + return evo->sem.bo; +} + +void +nvd0_display_flip_stop(struct drm_crtc *crtc) +{ + struct nvd0_display *disp = nvd0_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct evo *evo = &disp->evo[EVO_SYNC(nv_crtc->index)]; + u32 *push; + + push = evo_wait(crtc->dev, evo->idx, 8); + if (push) { + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0094, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, crtc->dev, evo->idx); + } +} + +int +nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, + struct nouveau_channel *chan, u32 swap_interval) +{ + struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); + struct nvd0_display *disp = nvd0_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct evo *evo = &disp->evo[EVO_SYNC(nv_crtc->index)]; + u64 offset; + u32 *push; + int ret; + + swap_interval <<= 4; + if (swap_interval == 0) + swap_interval |= 0x100; + + push = evo_wait(crtc->dev, evo->idx, 128); + if (unlikely(push == NULL)) + return -EBUSY; + + /* synchronise with the rendering channel, if necessary */ + if (likely(chan)) { + ret = RING_SPACE(chan, 10); + if (ret) + return ret; + + offset = chan->dispc_vma[nv_crtc->index].offset; + offset += evo->sem.offset; + + BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); + OUT_RING (chan, upper_32_bits(offset)); + OUT_RING (chan, lower_32_bits(offset)); + OUT_RING (chan, 0xf00d0000 | evo->sem.value); + OUT_RING (chan, 0x1002); + BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); + OUT_RING (chan, upper_32_bits(offset)); + OUT_RING (chan, lower_32_bits(offset ^ 0x10)); + OUT_RING (chan, 0x74b1e000); + OUT_RING (chan, 0x1001); + FIRE_RING (chan); + } else { + nouveau_bo_wr32(evo->sem.bo, evo->sem.offset / 4, + 0xf00d0000 | evo->sem.value); + evo_sync(crtc->dev, EVO_MASTER); + } + + /* queue the flip */ + evo_mthd(push, 0x0100, 1); + evo_data(push, 0xfffe0000); + evo_mthd(push, 0x0084, 1); + evo_data(push, swap_interval); + if (!(swap_interval & 0x00000100)) { + evo_mthd(push, 0x00e0, 1); + evo_data(push, 0x40000000); + } + evo_mthd(push, 0x0088, 4); + evo_data(push, evo->sem.offset); + evo_data(push, 0xf00d0000 | evo->sem.value); + evo_data(push, 0x74b1e000); + evo_data(push, NvEvoSync); + evo_mthd(push, 0x00a0, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, nv_fb->r_dma); + evo_mthd(push, 0x0110, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0400, 5); + evo_data(push, nv_fb->nvbo->bo.offset >> 8); + evo_data(push, 0); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nv_fb->r_pitch); + evo_data(push, nv_fb->r_format); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, crtc->dev, evo->idx); + + evo->sem.offset ^= 0x10; + evo->sem.value++; + return 0; +} + /****************************************************************************** * CRTC *****************************************************************************/ @@ -243,6 +397,7 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) { struct drm_display_mode *omode, *umode = &nv_crtc->base.mode; struct drm_device *dev = nv_crtc->base.dev; + struct drm_crtc *crtc = &nv_crtc->base; struct nouveau_connector *nv_connector; int mode = DRM_MODE_SCALE_NONE; u32 oX, oY, *push; @@ -308,7 +463,7 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) break; } - push = evo_wait(dev, EVO_MASTER, 16); + push = evo_wait(dev, EVO_MASTER, 8); if (push) { evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); evo_data(push, (oY << 16) | oX); @@ -318,11 +473,11 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) evo_data(push, 0x00000000); evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1); evo_data(push, (umode->vdisplay << 16) | umode->hdisplay); + evo_kick(push, dev, EVO_MASTER); if (update) { - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); + nvd0_display_flip_stop(crtc); + nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); } - evo_kick(push, dev, EVO_MASTER); } return 0; @@ -396,6 +551,8 @@ nvd0_crtc_prepare(struct drm_crtc *crtc) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); u32 *push; + nvd0_display_flip_stop(crtc); + push = evo_wait(crtc->dev, EVO_MASTER, 2); if (push) { evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); @@ -432,7 +589,8 @@ nvd0_crtc_commit(struct drm_crtc *crtc) evo_kick(push, crtc->dev, EVO_MASTER); } - nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true); + nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, false); + nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); } static bool @@ -524,6 +682,9 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); evo_data(push, syncs); evo_data(push, magic); + evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2); + evo_data(push, 0x00000311); + evo_data(push, 0x00000100); evo_kick(push, crtc->dev, EVO_MASTER); } @@ -550,7 +711,9 @@ nvd0_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, if (ret) return ret; + nvd0_display_flip_stop(crtc); nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, true); + nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); return 0; } @@ -560,6 +723,7 @@ nvd0_crtc_mode_set_base_atomic(struct drm_crtc *crtc, enum mode_set_atomic state) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + nvd0_display_flip_stop(crtc); nvd0_crtc_set_image(nv_crtc, fb, x, y, true); return 0; } @@ -675,6 +839,7 @@ static const struct drm_crtc_funcs nvd0_crtc_func = { .gamma_set = nvd0_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, .destroy = nvd0_crtc_destroy, + .page_flip = nouveau_crtc_page_flip, }; static void @@ -1572,8 +1737,10 @@ nvd0_display_destroy(struct drm_device *dev) int i; for (i = 0; i < 3; i++) { - pci_free_consistent(pdev, PAGE_SIZE, disp->evo[i].ptr, - disp->evo[i].handle); + struct evo *evo = &disp->evo[i]; + nouveau_bo_unmap(evo->sem.bo); + nouveau_bo_ref(NULL, &evo->sem.bo); + pci_free_consistent(pdev, PAGE_SIZE, evo->ptr, evo->handle); } nouveau_gpuobj_ref(NULL, &disp->mem); @@ -1654,54 +1821,77 @@ nvd0_display_create(struct drm_device *dev) 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, NvEvoSync); - nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001); - - 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); - nv_wo32(disp->mem, 0x1030, 0x00000000); - nv_wo32(disp->mem, 0x1034, 0x00000000); - nv_wo32(disp->mem, 0x0008, NvEvoVRAM); - 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 */ + /* create evo dma channels */ for (i = 0; i < 3; i++) { - disp->evo[i].ptr = pci_alloc_consistent(pdev, PAGE_SIZE, - &disp->evo[i].handle); - if (!disp->evo[i].ptr) { + struct evo *evo = &disp->evo[i]; + u32 dmao = 0x1000 + (i * 0x100); + u32 hash = 0x0000 + (i * 0x040); + u64 offset; + + evo->idx = i; + evo->ptr = pci_alloc_consistent(pdev, PAGE_SIZE, &evo->handle); + if (!evo->ptr) { ret = -ENOMEM; goto out; } + + ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, &evo->sem.bo); + if (!ret) { + ret = nouveau_bo_pin(evo->sem.bo, TTM_PL_FLAG_VRAM); + if (!ret) + ret = nouveau_bo_map(evo->sem.bo); + if (ret) + nouveau_bo_ref(NULL, &evo->sem.bo); + offset = evo->sem.bo->bo.offset; + } + + if (ret) + goto out; + + nv_wo32(disp->mem, dmao + 0x00, 0x00000049); + nv_wo32(disp->mem, dmao + 0x04, (offset + 0x0000) >> 8); + nv_wo32(disp->mem, dmao + 0x08, (offset + 0x0fff) >> 8); + nv_wo32(disp->mem, dmao + 0x0c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x10, 0x00000000); + nv_wo32(disp->mem, dmao + 0x14, 0x00000000); + nv_wo32(disp->mem, hash + 0x00, NvEvoSync); + nv_wo32(disp->mem, hash + 0x04, 0x00000001 | (i << 27) | + ((dmao + 0x00) << 9)); + + nv_wo32(disp->mem, dmao + 0x20, 0x00000049); + nv_wo32(disp->mem, dmao + 0x24, 0x00000000); + nv_wo32(disp->mem, dmao + 0x28, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, dmao + 0x2c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x30, 0x00000000); + nv_wo32(disp->mem, dmao + 0x34, 0x00000000); + nv_wo32(disp->mem, hash + 0x08, NvEvoVRAM); + nv_wo32(disp->mem, hash + 0x0c, 0x00000001 | (i << 27) | + ((dmao + 0x20) << 9)); + + nv_wo32(disp->mem, dmao + 0x40, 0x00000009); + nv_wo32(disp->mem, dmao + 0x44, 0x00000000); + nv_wo32(disp->mem, dmao + 0x48, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, dmao + 0x4c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x50, 0x00000000); + nv_wo32(disp->mem, dmao + 0x54, 0x00000000); + nv_wo32(disp->mem, hash + 0x10, NvEvoVRAM_LP); + nv_wo32(disp->mem, hash + 0x14, 0x00000001 | (i << 27) | + ((dmao + 0x40) << 9)); + + nv_wo32(disp->mem, dmao + 0x60, 0x0fe00009); + nv_wo32(disp->mem, dmao + 0x64, 0x00000000); + nv_wo32(disp->mem, dmao + 0x68, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, dmao + 0x6c, 0x00000000); + nv_wo32(disp->mem, dmao + 0x70, 0x00000000); + nv_wo32(disp->mem, dmao + 0x74, 0x00000000); + nv_wo32(disp->mem, hash + 0x18, NvEvoFB32); + nv_wo32(disp->mem, hash + 0x1c, 0x00000001 | (i << 27) | + ((dmao + 0x60) << 9)); } + pinstmem->flush(dev); + out: if (ret) nvd0_display_destroy(dev); -- cgit v1.2.3 From 8a46438a6a7492296c5dbfcc8e4a5106ab37bca8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 12 Nov 2011 23:52:07 +1000 Subject: drm/nvd0/disp: initialise overlay channels Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index dec1dd844d31..8fceebd110e6 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -35,8 +35,12 @@ #include "nouveau_fb.h" #include "nv50_display.h" +#define EVO_DMA_NR 9 + #define EVO_MASTER (0x00) #define EVO_SYNC(c) (0x01 + (c)) +#define EVO_OVLY(c) (0x05 + (c)) +#define EVO_OIMM(c) (0x09 + (c)) #define EVO_CURS(c) (0x0d + (c)) struct evo { @@ -52,7 +56,7 @@ struct evo { struct nvd0_display { struct nouveau_gpuobj *mem; - struct evo evo[3]; + struct evo evo[9]; struct tasklet_struct tasklet; u32 modeset; @@ -1642,9 +1646,11 @@ nvd0_display_fini(struct drm_device *dev) { int i; - /* fini cursors + syncs */ + /* fini cursors + overlays + syncs */ for (i = 1; i >= 0; i--) { evo_fini_pio(dev, EVO_CURS(i)); + evo_fini_pio(dev, EVO_OIMM(i)); + evo_fini_dma(dev, EVO_OVLY(i)); evo_fini_dma(dev, EVO_SYNC(i)); } @@ -1700,9 +1706,11 @@ nvd0_display_init(struct drm_device *dev) if (ret) goto error; - /* init syncs + cursors */ + /* init syncs + overlays + cursors */ for (i = 0; i < dev->mode_config.num_crtc; i++) { if ((ret = evo_init_dma(dev, EVO_SYNC(i))) || + (ret = evo_init_dma(dev, EVO_OVLY(i))) || + (ret = evo_init_pio(dev, EVO_OIMM(i))) || (ret = evo_init_pio(dev, EVO_CURS(i)))) goto error; } @@ -1736,7 +1744,7 @@ nvd0_display_destroy(struct drm_device *dev) struct pci_dev *pdev = dev->pdev; int i; - for (i = 0; i < 3; i++) { + for (i = 0; i < EVO_DMA_NR; i++) { struct evo *evo = &disp->evo[i]; nouveau_bo_unmap(evo->sem.bo); nouveau_bo_ref(NULL, &evo->sem.bo); @@ -1822,7 +1830,7 @@ nvd0_display_create(struct drm_device *dev) goto out; /* create evo dma channels */ - for (i = 0; i < 3; i++) { + for (i = 0; i < EVO_DMA_NR; i++) { struct evo *evo = &disp->evo[i]; u32 dmao = 0x1000 + (i * 0x100); u32 hash = 0x0000 + (i * 0x040); -- cgit v1.2.3 From 84e052e626d88c04c03f79dd95708e556d018d97 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 13 Nov 2011 03:43:30 +1000 Subject: drm/nvd0/disp: handle yet another interrupt Spotted while messing with overlay channels (probably as a result of sending a similar "disable" sequence as we do for the flip channels). The value in 0x61008c was 0x20, which one would reasonably guess is "bit 5 == something to report about evo channel 5" - but who knows. Spotted the binary driver getting this too, and it appears to not do anything exciting as a result. So, handle it the same way and avoid an IRQ storm. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 8fceebd110e6..51e073828471 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -1585,6 +1585,12 @@ nvd0_display_intr(struct drm_device *dev) struct nvd0_display *disp = nvd0_display(dev); u32 intr = nv_rd32(dev, 0x610088); + if (intr & 0x00000001) { + u32 stat = nv_rd32(dev, 0x61008c); + nv_wr32(dev, 0x61008c, stat); + intr &= ~0x00000001; + } + if (intr & 0x00000002) { u32 stat = nv_rd32(dev, 0x61009c); int chid = ffs(stat) - 1; -- cgit v1.2.3 From a63a97eb6684b280a11161c28ed5f4bbb14a5ffa Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 16 Nov 2011 15:22:34 +1000 Subject: drm/nvd0/disp: rename sync channel to flip channel Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 51e073828471..4002e9f82af8 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -38,7 +38,7 @@ #define EVO_DMA_NR 9 #define EVO_MASTER (0x00) -#define EVO_SYNC(c) (0x01 + (c)) +#define EVO_FLIP(c) (0x01 + (c)) #define EVO_OVLY(c) (0x05 + (c)) #define EVO_OIMM(c) (0x09 + (c)) #define EVO_CURS(c) (0x0d + (c)) @@ -240,13 +240,13 @@ evo_sync(struct drm_device *dev, int ch) } /****************************************************************************** - * Sync channel (aka. page flipping) + * Page flipping channel *****************************************************************************/ struct nouveau_bo * nvd0_display_crtc_sema(struct drm_device *dev, int crtc) { struct nvd0_display *disp = nvd0_display(dev); - struct evo *evo = &disp->evo[EVO_SYNC(crtc)]; + struct evo *evo = &disp->evo[EVO_FLIP(crtc)]; return evo->sem.bo; } @@ -255,7 +255,7 @@ nvd0_display_flip_stop(struct drm_crtc *crtc) { struct nvd0_display *disp = nvd0_display(crtc->dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct evo *evo = &disp->evo[EVO_SYNC(nv_crtc->index)]; + struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)]; u32 *push; push = evo_wait(crtc->dev, evo->idx, 8); @@ -279,7 +279,7 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); struct nvd0_display *disp = nvd0_display(crtc->dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct evo *evo = &disp->evo[EVO_SYNC(nv_crtc->index)]; + struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)]; u64 offset; u32 *push; int ret; @@ -1652,12 +1652,12 @@ nvd0_display_fini(struct drm_device *dev) { int i; - /* fini cursors + overlays + syncs */ + /* fini cursors + overlays + flips */ for (i = 1; i >= 0; i--) { evo_fini_pio(dev, EVO_CURS(i)); evo_fini_pio(dev, EVO_OIMM(i)); evo_fini_dma(dev, EVO_OVLY(i)); - evo_fini_dma(dev, EVO_SYNC(i)); + evo_fini_dma(dev, EVO_FLIP(i)); } /* fini master */ @@ -1712,9 +1712,9 @@ nvd0_display_init(struct drm_device *dev) if (ret) goto error; - /* init syncs + overlays + cursors */ + /* init flips + overlays + cursors */ for (i = 0; i < dev->mode_config.num_crtc; i++) { - if ((ret = evo_init_dma(dev, EVO_SYNC(i))) || + if ((ret = evo_init_dma(dev, EVO_FLIP(i))) || (ret = evo_init_dma(dev, EVO_OVLY(i))) || (ret = evo_init_pio(dev, EVO_OIMM(i))) || (ret = evo_init_pio(dev, EVO_CURS(i)))) -- cgit v1.2.3 From 816af2f2d6bf3191988ecaf2340452ebc46025fc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 16 Nov 2011 15:48:48 +1000 Subject: drm/nvd0/disp: use single, shared, sync bo for all evo channels This simplifies some things, and hopefully won't come back to bite me. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 63 +++++++++++++++++----------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 4002e9f82af8..89a31851e83a 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -43,12 +43,17 @@ #define EVO_OIMM(c) (0x09 + (c)) #define EVO_CURS(c) (0x0d + (c)) +/* offsets in shared sync bo of various structures */ +#define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) +#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) +#define EVO_FLIP_SEM0(c) EVO_SYNC((c), 0x00) +#define EVO_FLIP_SEM1(c) EVO_SYNC((c), 0x10) + struct evo { int idx; dma_addr_t handle; u32 *ptr; struct { - struct nouveau_bo *bo; u32 offset; u16 value; } sem; @@ -56,6 +61,7 @@ struct evo { struct nvd0_display { struct nouveau_gpuobj *mem; + struct nouveau_bo *sync; struct evo evo[9]; struct tasklet_struct tasklet; @@ -212,27 +218,23 @@ evo_fini_pio(struct drm_device *dev, int ch) static bool evo_sync_wait(void *data) { - return nouveau_bo_rd32(data, 0) != 0x00000000; + return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000; } static int evo_sync(struct drm_device *dev, int ch) { struct nvd0_display *disp = nvd0_display(dev); - struct evo *evo = &disp->evo[ch]; - u32 *push; - - nouveau_bo_wr32(evo->sem.bo, 0, 0x00000000); - - push = evo_wait(dev, ch, 8); + u32 *push = evo_wait(dev, ch, 8); if (push) { + nouveau_bo_wr32(disp->sync, EVO_MAST_NTFY, 0x00000000); evo_mthd(push, 0x0084, 1); - evo_data(push, 0x80000000); + evo_data(push, 0x80000000 | EVO_MAST_NTFY); evo_mthd(push, 0x0080, 2); evo_data(push, 0x00000000); evo_data(push, 0x00000000); evo_kick(push, dev, ch); - if (nv_wait_cb(dev, evo_sync_wait, evo->sem.bo)) + if (nv_wait_cb(dev, evo_sync_wait, disp->sync)) return 0; } @@ -245,9 +247,7 @@ evo_sync(struct drm_device *dev, int ch) struct nouveau_bo * nvd0_display_crtc_sema(struct drm_device *dev, int crtc) { - struct nvd0_display *disp = nvd0_display(dev); - struct evo *evo = &disp->evo[EVO_FLIP(crtc)]; - return evo->sem.bo; + return nvd0_display(dev)->sync; } void @@ -313,7 +313,7 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, OUT_RING (chan, 0x1001); FIRE_RING (chan); } else { - nouveau_bo_wr32(evo->sem.bo, evo->sem.offset / 4, + nouveau_bo_wr32(disp->sync, evo->sem.offset / 4, 0xf00d0000 | evo->sem.value); evo_sync(crtc->dev, EVO_MASTER); } @@ -1752,12 +1752,12 @@ nvd0_display_destroy(struct drm_device *dev) for (i = 0; i < EVO_DMA_NR; i++) { struct evo *evo = &disp->evo[i]; - nouveau_bo_unmap(evo->sem.bo); - nouveau_bo_ref(NULL, &evo->sem.bo); pci_free_consistent(pdev, PAGE_SIZE, evo->ptr, evo->handle); } nouveau_gpuobj_ref(NULL, &disp->mem); + nouveau_bo_unmap(disp->sync); + nouveau_bo_ref(NULL, &disp->sync); nouveau_irq_unregister(dev, 26); dev_priv->engine.display.priv = NULL; @@ -1829,6 +1829,20 @@ nvd0_display_create(struct drm_device *dev) tasklet_init(&disp->tasklet, nvd0_display_bh, (unsigned long)dev); nouveau_irq_register(dev, 26, nvd0_display_intr); + /* small shared memory area we use for notifiers and semaphores */ + ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, &disp->sync); + if (!ret) { + ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM); + if (!ret) + ret = nouveau_bo_map(disp->sync); + if (ret) + nouveau_bo_ref(NULL, &disp->sync); + } + + if (ret) + goto out; + /* hash table and dma objects for the memory areas we care about */ ret = nouveau_gpuobj_new(dev, NULL, 0x4000, 0x10000, NVOBJ_FLAG_ZERO_ALLOC, &disp->mem); @@ -1838,31 +1852,18 @@ nvd0_display_create(struct drm_device *dev) /* create evo dma channels */ for (i = 0; i < EVO_DMA_NR; i++) { struct evo *evo = &disp->evo[i]; + u64 offset = disp->sync->bo.offset; u32 dmao = 0x1000 + (i * 0x100); u32 hash = 0x0000 + (i * 0x040); - u64 offset; evo->idx = i; + evo->sem.offset = EVO_SYNC(evo->idx, 0x00); evo->ptr = pci_alloc_consistent(pdev, PAGE_SIZE, &evo->handle); if (!evo->ptr) { ret = -ENOMEM; goto out; } - ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, &evo->sem.bo); - if (!ret) { - ret = nouveau_bo_pin(evo->sem.bo, TTM_PL_FLAG_VRAM); - if (!ret) - ret = nouveau_bo_map(evo->sem.bo); - if (ret) - nouveau_bo_ref(NULL, &evo->sem.bo); - offset = evo->sem.bo->bo.offset; - } - - if (ret) - goto out; - nv_wo32(disp->mem, dmao + 0x00, 0x00000049); nv_wo32(disp->mem, dmao + 0x04, (offset + 0x0000) >> 8); nv_wo32(disp->mem, dmao + 0x08, (offset + 0x0fff) >> 8); -- cgit v1.2.3 From f9f9f536312d4c3ca39502ccf6a3af60cfe38ff4 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 12 Oct 2011 16:48:48 +1000 Subject: drm/nouveau/bios: pass drm_device to ROMPTR, rather than nvbios Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 10 +++++----- drivers/gpu/drm/nouveau/nouveau_bios.h | 11 ++++++++--- drivers/gpu/drm/nouveau/nouveau_dp.c | 9 +++------ drivers/gpu/drm/nouveau/nouveau_mem.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_perf.c | 11 +++++------ drivers/gpu/drm/nouveau/nouveau_temp.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_volt.c | 6 +++--- 7 files changed, 28 insertions(+), 27 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 89747bebbfc7..dfa8e329c876 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -4541,7 +4541,7 @@ 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); for (i = 0; i < table[3]; i++) { - otable = ROMPTR(bios, table[table[1] + (i * table[2])]); + otable = ROMPTR(dev, table[table[1] + (i * table[2])]); if (otable && bios_encoder_match(dcbent, ROM32(otable[0]))) break; } @@ -5493,7 +5493,7 @@ bit_table(struct drm_device *dev, u8 id, struct bit_entry *bit) bit->version = entry[1]; bit->length = ROM16(entry[2]); bit->offset = ROM16(entry[4]); - bit->data = ROMPTR(bios, entry[4]); + bit->data = ROMPTR(dev, entry[4]); return 0; } @@ -5807,9 +5807,9 @@ parse_dcb_gpio_table(struct nvbios *bios) u8 *dcb, *gpio = NULL, *entry; int i; - dcb = ROMPTR(bios, bios->data[0x36]); + dcb = ROMPTR(dev, bios->data[0x36]); if (dcb[0] >= 0x30) { - gpio = ROMPTR(bios, dcb[10]); + gpio = ROMPTR(dev, dcb[10]); if (!gpio) goto no_table; @@ -5818,7 +5818,7 @@ parse_dcb_gpio_table(struct nvbios *bios) recordlen = gpio[3]; } else if (dcb[0] >= 0x22 && dcb[-1] >= 0x13) { - gpio = ROMPTR(bios, dcb[-15]); + gpio = ROMPTR(dev, dcb[-15]); if (!gpio) goto no_table; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index dff121d8896f..5f1258834ec1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -34,9 +34,14 @@ #define DCB_LOC_ON_CHIP 0 -#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x)) -#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x)) -#define ROMPTR(bios, x) (ROM16(x) ? &(bios)->data[ROM16(x)] : NULL) +#define ROM16(x) le16_to_cpu(*(u16 *)&(x)) +#define ROM32(x) le32_to_cpu(*(u32 *)&(x)) +#define ROM48(x) ({ u8 *p = &(x); (u64)ROM16(p[4]) << 32 | ROM32(p[0]); }) +#define ROM64(x) le64_to_cpu(*(u64 *)&(x)) +#define ROMPTR(d,x) ({ \ + struct drm_nouveau_private *dev_priv = (d)->dev_private; \ + ROM16(x) ? &dev_priv->vbios.data[ROM16(x)] : NULL; \ +}) struct bit_entry { uint8_t id; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index de5efe71fefd..5722fd55764b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -273,8 +273,6 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) 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; @@ -289,7 +287,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) return NULL; } - table = ROMPTR(bios, d.data[0]); + table = ROMPTR(dev, d.data[0]); if (!table) { NV_ERROR(dev, "displayport table pointer invalid\n"); return NULL; @@ -306,7 +304,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) } for (i = 0; i < table[3]; i++) { - *entry = ROMPTR(bios, table[table[1] + (i * table[2])]); + *entry = ROMPTR(dev, table[table[1] + (i * table[2])]); if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0]))) return table; } @@ -336,7 +334,6 @@ 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; u8 *entry, sink[2]; u32 dp_ctrl; @@ -360,7 +357,7 @@ 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. */ - entry = ROMPTR(&dev_priv->vbios, dp->entry[10]); + entry = ROMPTR(dev, dp->entry[10]); if (entry) { if (dp->table[0] < 0x30) { while (dp->link_bw < (ROM16(entry[0]) * 10)) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 37fcaa260e98..c3a5745e9c79 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -644,10 +644,10 @@ nouveau_mem_timing_init(struct drm_device *dev) return; if (P.version == 1) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[4]); else if (P.version == 2) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[8]); else { NV_WARN(dev, "unknown mem for BIT P %d\n", P.version); } diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 6d49bdbf93d0..58f497343cec 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -41,7 +41,7 @@ legacy_perf_init(struct drm_device *dev) return; } - perf = ROMPTR(bios, bmp[0x73]); + perf = ROMPTR(dev, bmp[0x73]); if (!perf) { NV_DEBUG(dev, "No memclock table pointer found.\n"); return; @@ -87,7 +87,7 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, * ramcfg to select the correct subentry */ if (P->version == 2) { - u8 *tmap = ROMPTR(bios, P->data[4]); + u8 *tmap = ROMPTR(dev, P->data[4]); if (!tmap) { NV_DEBUG(dev, "no timing map pointer\n"); return NULL; @@ -140,7 +140,6 @@ 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; @@ -165,7 +164,7 @@ nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, return; } - vmap = ROMPTR(bios, P->data[32]); + vmap = ROMPTR(dev, P->data[32]); if (!vmap) { NV_DEBUG(dev, "volt map table pointer invalid\n"); return; @@ -200,7 +199,7 @@ nouveau_perf_init(struct drm_device *dev) return; } - perf = ROMPTR(bios, P.data[0]); + perf = ROMPTR(dev, P.data[0]); version = perf[0]; headerlen = perf[1]; if (version < 0x40) { @@ -218,7 +217,7 @@ nouveau_perf_init(struct drm_device *dev) return; } - perf = ROMPTR(bios, bios->data[bios->offset + 0x94]); + perf = ROMPTR(dev, bios->data[bios->offset + 0x94]); if (!perf) { NV_DEBUG(dev, "perf table pointer invalid\n"); return; diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 6364e4c46124..4c46adeb0715 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -316,9 +316,9 @@ nouveau_temp_init(struct drm_device *dev) return; if (P.version == 1) - temp = ROMPTR(bios, P.data[12]); + temp = ROMPTR(dev, P.data[12]); else if (P.version == 2) - temp = ROMPTR(bios, P.data[16]); + temp = ROMPTR(dev, P.data[16]); else NV_WARN(dev, "unknown temp for BIT P %d\n", P.version); diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 86d03e15735d..ac15b46ea3a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -117,10 +117,10 @@ nouveau_volt_init(struct drm_device *dev) return; if (P.version == 1) - volt = ROMPTR(bios, P.data[16]); + volt = ROMPTR(dev, P.data[16]); else if (P.version == 2) - volt = ROMPTR(bios, P.data[12]); + volt = ROMPTR(dev, P.data[12]); else { NV_WARN(dev, "unknown volt for BIT P %d\n", P.version); } @@ -130,7 +130,7 @@ nouveau_volt_init(struct drm_device *dev) return; } - volt = ROMPTR(bios, bios->data[bios->offset + 0x98]); + volt = ROMPTR(dev, bios->data[bios->offset + 0x98]); } if (!volt) { -- cgit v1.2.3 From 0f8067c7054d22f240fca376e01430eecdc112df Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 10 Nov 2011 14:49:19 +1000 Subject: drm/nouveau/bios: fold fixup_legacy_i2c Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index dfa8e329c876..61c5c3240e31 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -34,9 +34,6 @@ #define NV_CIO_CRE_44_HEADA 0x0 #define NV_CIO_CRE_44_HEADB 0x3 #define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */ -#define LEGACY_I2C_CRT 0x80 -#define LEGACY_I2C_PANEL 0x81 -#define LEGACY_I2C_TV 0x82 #define EDID1_LEN 128 @@ -6433,15 +6430,18 @@ fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios) #endif /* Make up some sane defaults */ - fabricate_dcb_output(dcb, OUTPUT_ANALOG, LEGACY_I2C_CRT, 1, 1); + fabricate_dcb_output(dcb, OUTPUT_ANALOG, + bios->legacy.i2c_indices.crt, 1, 1); if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0) - fabricate_dcb_output(dcb, OUTPUT_TV, LEGACY_I2C_TV, + fabricate_dcb_output(dcb, OUTPUT_TV, + bios->legacy.i2c_indices.tv, all_heads, 0); else if (bios->tmds.output0_script_ptr || bios->tmds.output1_script_ptr) - fabricate_dcb_output(dcb, OUTPUT_TMDS, LEGACY_I2C_PANEL, + fabricate_dcb_output(dcb, OUTPUT_TMDS, + bios->legacy.i2c_indices.panel, all_heads, 1); } @@ -6649,22 +6649,6 @@ fixup_legacy_connector(struct nvbios *bios) } } -static void -fixup_legacy_i2c(struct nvbios *bios) -{ - struct dcb_table *dcb = &bios->dcb; - int i; - - for (i = 0; i < dcb->entries; i++) { - if (dcb->entry[i].i2c_index == LEGACY_I2C_CRT) - dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt; - if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL) - dcb->entry[i].i2c_index = bios->legacy.i2c_indices.panel; - if (dcb->entry[i].i2c_index == LEGACY_I2C_TV) - dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv; - } -} - static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry) { /* @@ -6934,7 +6918,6 @@ nouveau_bios_init(struct drm_device *dev) if (ret) return ret; - fixup_legacy_i2c(bios); fixup_legacy_connector(bios); if (!bios->major_version) /* we don't run version 0 bios */ -- cgit v1.2.3 From 6b5a81a2e783f26a69fc262b3c393f0b391c8613 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 10 Nov 2011 15:42:55 +1000 Subject: drm/nouveau/bios: start refactoring dcb routines This primary reason for this was mostly to avoid duplication of some of this stuff by the MXM-SIS parser. However, some other cleanups will also follow this as a result. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 270 ++++++++++++++++++--------------- drivers/gpu/drm/nouveau/nouveau_bios.h | 6 + drivers/gpu/drm/nouveau/nouveau_drv.h | 7 + 3 files changed, 162 insertions(+), 121 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 61c5c3240e31..c7723fb54077 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -6048,6 +6048,109 @@ parse_dcb_connector_table(struct nvbios *bios) } } +void * +dcb_table(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u8 *dcb = NULL; + + if (dev_priv->card_type > NV_04) + dcb = ROMPTR(dev, dev_priv->vbios.data[0x36]); + if (!dcb) { + NV_WARNONCE(dev, "No DCB data found in VBIOS\n"); + return NULL; + } + + if (dcb[0] >= 0x41) { + NV_WARNONCE(dev, "DCB version 0x%02x unknown\n", dcb[0]); + return NULL; + } else + if (dcb[0] >= 0x30) { + if (ROM32(dcb[6]) == 0x4edcbdcb) + return dcb; + } else + if (dcb[0] >= 0x20) { + if (ROM32(dcb[4]) == 0x4edcbdcb) + return dcb; + } else + if (dcb[0] >= 0x15) { + if (!memcmp(&dcb[-7], "DEV_REC", 7)) + return dcb; + } else { + /* + * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but + * always has the same single (crt) entry, even when tv-out + * present, so the conclusion is this version cannot really + * be used. + * + * v1.2 tables (some NV6/10, and NV15+) normally have the + * same 5 entries, which are not specific to the card and so + * no use. + * + * v1.2 does have an I2C table that read_dcb_i2c_table can + * handle, but cards exist (nv11 in #14821) with a bad i2c + * table pointer, so use the indices parsed in + * parse_bmp_structure. + * + * v1.1 (NV5+, maybe some NV4) is entirely unhelpful + */ + NV_WARNONCE(dev, "No useful DCB data in VBIOS\n"); + return NULL; + } + + NV_WARNONCE(dev, "DCB header validation failed\n"); + return NULL; +} + +u8 * +dcb_outp(struct drm_device *dev, u8 idx) +{ + u8 *dcb = dcb_table(dev); + if (dcb && dcb[0] >= 0x30) { + if (idx < dcb[2]) + return dcb + dcb[1] + (idx * dcb[3]); + } else + if (dcb && dcb[0] >= 0x20) { + u8 *i2c = ROMPTR(dev, dcb[2]); + u8 *ent = dcb + 8 + (idx * 8); + if (i2c && ent < i2c) + return ent; + } else + if (dcb && dcb[0] >= 0x15) { + u8 *i2c = ROMPTR(dev, dcb[2]); + u8 *ent = dcb + 4 + (idx * 10); + if (i2c && ent < i2c) + return ent; + } + + return NULL; +} + +int +dcb_outp_foreach(struct drm_device *dev, void *data, + int (*exec)(struct drm_device *, void *, int idx, u8 *outp)) +{ + int ret, idx = -1; + u8 *outp = NULL; + while ((outp = dcb_outp(dev, ++idx))) { + if (ROM32(outp[0]) == 0x00000000) + break; /* seen on an NV11 with DCB v1.5 */ + if (ROM32(outp[0]) == 0xffffffff) + break; /* seen on an NV17 with DCB v2.0 */ + + if ((outp[0] & 0x0f) == OUTPUT_UNUSED) + continue; + if ((outp[0] & 0x0f) == OUTPUT_EOL) + break; + + ret = exec(dev, data, idx, outp); + if (ret) + return ret; + } + + return 0; +} + static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb) { struct dcb_entry *entry = &dcb->entry[dcb->entries]; @@ -6251,25 +6354,6 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb, return true; } -static bool parse_dcb_entry(struct drm_device *dev, struct dcb_table *dcb, - uint32_t conn, uint32_t conf) -{ - struct dcb_entry *entry = new_dcb_entry(dcb); - bool ret; - - if (dcb->version >= 0x20) - ret = parse_dcb20_entry(dev, dcb, conn, conf, entry); - else - ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); - if (!ret) - return ret; - - read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, - entry->i2c_index, &dcb->i2c[entry->i2c_index]); - - return true; -} - static void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb) { @@ -6446,88 +6530,62 @@ fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios) } static int -parse_dcb_table(struct drm_device *dev, struct nvbios *bios) +parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_table *dcb = &bios->dcb; - uint16_t dcbptr = 0, i2ctabptr = 0; - uint8_t *dcbtable; - uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES; - bool configblock = true; - int recordlength = 8, confofs = 4; - int i; + struct dcb_table *dcb = &dev_priv->vbios.dcb; + u32 conf = (dcb->version >= 0x20) ? ROM32(outp[4]) : ROM32(outp[6]); + u32 conn = ROM32(outp[0]); + bool ret; - /* get the offset from 0x36 */ - if (dev_priv->card_type > NV_04) { - dcbptr = ROM16(bios->data[0x36]); - if (dcbptr == 0x0000) - NV_WARN(dev, "No output data (DCB) found in BIOS\n"); - } + if (apply_dcb_encoder_quirks(dev, idx, &conn, &conf)) { + struct dcb_entry *entry = new_dcb_entry(dcb); - /* this situation likely means a really old card, pre DCB */ - if (dcbptr == 0x0) { - fabricate_dcb_encoder_table(dev, bios); - return 0; - } + NV_TRACEWARN(dev, "DCB entry %02d: %08x %08x\n", idx, conn, conf); - dcbtable = &bios->data[dcbptr]; + if (dcb->version >= 0x20) + ret = parse_dcb20_entry(dev, dcb, conn, conf, entry); + else + ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); + if (!ret) + return 1; /* stop parsing */ - /* get DCB version */ - dcb->version = dcbtable[0]; - NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n", - dcb->version >> 4, dcb->version & 0xf); + read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, + entry->i2c_index, + &dcb->i2c[entry->i2c_index]); + } - if (dcb->version >= 0x20) { /* NV17+ */ - uint32_t sig; + return 0; +} - if (dcb->version >= 0x30) { /* NV40+ */ - headerlen = dcbtable[1]; - entries = dcbtable[2]; - recordlength = dcbtable[3]; - i2ctabptr = ROM16(dcbtable[4]); - sig = ROM32(dcbtable[6]); - dcb->gpio_table_ptr = ROM16(dcbtable[10]); - dcb->connector_table_ptr = ROM16(dcbtable[20]); - } else { - i2ctabptr = ROM16(dcbtable[2]); - sig = ROM32(dcbtable[4]); - headerlen = 8; +static int +parse_dcb_table(struct drm_device *dev, struct nvbios *bios) +{ + struct dcb_table *dcb = &bios->dcb; + u16 i2ctabptr = 0x0000; + u8 *dcbt; + + dcbt = dcb_table(dev); + if (!dcbt) { + /* handle pre-DCB boards */ + if (bios->type == NVBIOS_BMP) { + fabricate_dcb_encoder_table(dev, bios); + return 0; } - if (sig != 0x4edcbdcb) { - NV_ERROR(dev, "Bad Display Configuration Block " - "signature (%08X)\n", sig); - return -EINVAL; - } - } else if (dcb->version >= 0x15) { /* some NV11 and NV20 */ - char sig[8] = { 0 }; + return -EINVAL; + } - strncpy(sig, (char *)&dcbtable[-7], 7); - i2ctabptr = ROM16(dcbtable[2]); - recordlength = 10; - confofs = 6; + NV_TRACE(dev, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf); - if (strcmp(sig, "DEV_REC")) { - NV_ERROR(dev, "Bad Display Configuration Block " - "signature (%s)\n", sig); - return -EINVAL; - } - } else { - /* - * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always - * has the same single (crt) entry, even when tv-out present, so - * the conclusion is this version cannot really be used. - * v1.2 tables (some NV6/10, and NV15+) normally have the same - * 5 entries, which are not specific to the card and so no use. - * v1.2 does have an I2C table that read_dcb_i2c_table can - * handle, but cards exist (nv11 in #14821) with a bad i2c table - * pointer, so use the indices parsed in parse_bmp_structure. - * v1.1 (NV5+, maybe some NV4) is entirely unhelpful - */ - NV_TRACEWARN(dev, "No useful information in BIOS output table; " - "adding all possible outputs\n"); - fabricate_dcb_encoder_table(dev, bios); - return 0; + dcb->version = dcbt[0]; + if (dcb->version >= 0x30) { + i2ctabptr = ROM16(dcbt[4]); + dcb->gpio_table_ptr = ROM16(dcbt[10]); + dcb->connector_table_ptr = ROM16(dcbt[20]); + } else + if (dcb->version >= 0x15) { + i2ctabptr = ROM16(dcbt[2]); } if (!i2ctabptr) @@ -6543,44 +6601,14 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) */ if (dcb->version >= 0x22) { int idx = (dcb->version >= 0x40 ? - dcb->i2c_default_indices & 0xf : - 2); + dcb->i2c_default_indices & 0xf : 2); read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, idx, &dcb->i2c[idx]); } } - if (entries > DCB_MAX_NUM_ENTRIES) - entries = DCB_MAX_NUM_ENTRIES; - - for (i = 0; i < entries; i++) { - uint32_t connection, config = 0; - - connection = ROM32(dcbtable[headerlen + recordlength * i]); - if (configblock) - config = ROM32(dcbtable[headerlen + confofs + recordlength * i]); - - /* seen on an NV11 with DCB v1.5 */ - if (connection == 0x00000000) - break; - - /* seen on an NV17 with DCB v2.0 */ - if (connection == 0xffffffff) - break; - - if ((connection & 0x0000000f) == 0x0000000f) - continue; - - if (!apply_dcb_encoder_quirks(dev, i, &connection, &config)) - continue; - - NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n", - dcb->entries, connection, config); - - if (!parse_dcb_entry(dev, dcb, connection, config)) - break; - } + dcb_outp_foreach(dev, NULL, parse_dcb_entry); /* * apart for v2.1+ not being known for requiring merging, this diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 5f1258834ec1..b9f2da394f89 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -117,6 +117,7 @@ enum dcb_type { OUTPUT_LVDS = 3, OUTPUT_DP = 6, OUTPUT_EOL = 14, /* DCB 4.0+, appears to be end-of-list */ + OUTPUT_UNUSED = 15, OUTPUT_ANY = -1 }; @@ -339,4 +340,9 @@ struct nvbios { } legacy; }; +void *dcb_table(struct drm_device *); +u8 *dcb_outp(struct drm_device *, u8 idx); +int dcb_outp_foreach(struct drm_device *, void *data, + int (*)(struct drm_device *, void *, int idx, u8 *outp)); + #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 1e70005d8220..446caedbcff9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1608,6 +1608,13 @@ extern void nv_wo32(struct nouveau_gpuobj *, u32 offset, u32 val); #define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg) #define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg) #define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg) +#define NV_WARNONCE(d, fmt, arg...) do { \ + static int _warned = 0; \ + if (!_warned) { \ + NV_WARN(d, fmt, ##arg); \ + _warned = 1; \ + } \ +} while(0) /* nouveau_reg_debug bitmask */ enum { -- cgit v1.2.3 From 486a45c2a6c19b159602d044ab601a92cd81f524 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 10:22:19 +1000 Subject: drm/nouveau/i2c: do parsing of i2c-related vbios info in nouveau_i2c.c Not much point parsing the vbios data into a struct which is only used once to parse the data into another struct, go directly from vbios to nouveau_i2c_chan. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 166 ++---------------- drivers/gpu/drm/nouveau/nouveau_bios.h | 11 -- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_i2c.c | 302 ++++++++++++++++++++++----------- drivers/gpu/drm/nouveau/nouveau_i2c.h | 14 +- drivers/gpu/drm/nouveau/nouveau_temp.c | 6 +- 6 files changed, 222 insertions(+), 278 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index c7723fb54077..7922bb969d25 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -720,116 +720,20 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev) return dcb_entry; } -static int -read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c) -{ - uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4; - int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES; - int recordoffset = 0, rdofs = 1, wrofs = 0; - uint8_t port_type = 0; - - if (!i2ctable) - return -EINVAL; - - if (dcb_version >= 0x30) { - if (i2ctable[0] != dcb_version) /* necessary? */ - NV_WARN(dev, - "DCB I2C table version mismatch (%02X vs %02X)\n", - i2ctable[0], dcb_version); - dcb_i2c_ver = i2ctable[0]; - headerlen = i2ctable[1]; - if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES) - i2c_entries = i2ctable[2]; - else - NV_WARN(dev, - "DCB I2C table has more entries than indexable " - "(%d entries, max %d)\n", i2ctable[2], - DCB_MAX_NUM_I2C_ENTRIES); - entry_len = i2ctable[3]; - /* [4] is i2c_default_indices, read in parse_dcb_table() */ - } - /* - * It's your own fault if you call this function on a DCB 1.1 BIOS -- - * the test below is for DCB 1.2 - */ - if (dcb_version < 0x14) { - recordoffset = 2; - rdofs = 0; - wrofs = 1; - } - - if (index == 0xf) - return 0; - if (index >= i2c_entries) { - NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n", - index, i2ctable[2]); - return -ENOENT; - } - if (i2ctable[headerlen + entry_len * index + 3] == 0xff) { - NV_ERROR(dev, "DCB I2C entry invalid\n"); - return -EINVAL; - } - - if (dcb_i2c_ver >= 0x30) { - port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index]; - - /* - * Fixup for chips using same address offset for read and - * write. - */ - if (port_type == 4) /* seen on C51 */ - rdofs = wrofs = 1; - if (port_type >= 5) /* G80+ */ - rdofs = wrofs = 0; - } - - if (dcb_i2c_ver >= 0x40) { - if (port_type != 5 && port_type != 6) - NV_WARN(dev, "DCB I2C table has port type %d\n", port_type); - - i2c->entry = ROM32(i2ctable[headerlen + recordoffset + entry_len * index]); - } - - i2c->port_type = port_type; - i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index]; - i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index]; - - return 0; -} - static struct nouveau_i2c_chan * init_i2c_device_find(struct drm_device *dev, int i2c_index) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_table *dcb = &dev_priv->vbios.dcb; - if (i2c_index == 0xff) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct dcb_table *dcb = &dev_priv->vbios.dcb; /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */ - int idx = dcb_entry_idx_from_crtchead(dev), shift = 0; - int default_indices = dcb->i2c_default_indices; + int idx = dcb_entry_idx_from_crtchead(dev); + i2c_index = NV_I2C_DEFAULT(0); if (idx != 0x7f && dcb->entry[idx].i2c_upper_default) - shift = 4; - - i2c_index = (default_indices >> shift) & 0xf; - } - if (i2c_index == 0x80) /* g80+ */ - i2c_index = dcb->i2c_default_indices & 0xf; - else - if (i2c_index == 0x81) - i2c_index = (dcb->i2c_default_indices & 0xf0) >> 4; - - if (i2c_index >= DCB_MAX_NUM_I2C_ENTRIES) { - NV_ERROR(dev, "invalid i2c_index 0x%x\n", i2c_index); - return NULL; + i2c_index = NV_I2C_DEFAULT(1); } - /* Make sure i2c table entry has been parsed, it may not - * have been if this is a bus not referenced by a DCB encoder - */ - read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, - i2c_index, &dcb->i2c[i2c_index]); - return nouveau_i2c_find(dev, i2c_index); } @@ -5595,10 +5499,6 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi uint16_t legacy_scripts_offset, legacy_i2c_offset; /* load needed defaults in case we can't parse this info */ - bios->dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX; - bios->dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX; - bios->dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX; - bios->dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX; bios->digital_min_front_porch = 0x4b; bios->fmaxvco = 256000; bios->fminvco = 128000; @@ -5706,14 +5606,6 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset]; bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1]; bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2]; - if (bios->data[legacy_i2c_offset + 4]) - bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4]; - if (bios->data[legacy_i2c_offset + 5]) - bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5]; - if (bios->data[legacy_i2c_offset + 6]) - bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6]; - if (bios->data[legacy_i2c_offset + 7]) - bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7]; if (bmplength > 74) { bios->fmaxvco = ROM32(bmp[67]); @@ -6549,10 +6441,6 @@ parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp) ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); if (!ret) return 1; /* stop parsing */ - - read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, - entry->i2c_index, - &dcb->i2c[entry->i2c_index]); } return 0; @@ -6562,7 +6450,6 @@ static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios) { struct dcb_table *dcb = &bios->dcb; - u16 i2ctabptr = 0x0000; u8 *dcbt; dcbt = dcb_table(dev); @@ -6580,32 +6467,8 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) dcb->version = dcbt[0]; if (dcb->version >= 0x30) { - i2ctabptr = ROM16(dcbt[4]); dcb->gpio_table_ptr = ROM16(dcbt[10]); dcb->connector_table_ptr = ROM16(dcbt[20]); - } else - if (dcb->version >= 0x15) { - i2ctabptr = ROM16(dcbt[2]); - } - - if (!i2ctabptr) - NV_WARN(dev, "No pointer to DCB I2C port table\n"); - else { - dcb->i2c_table = &bios->data[i2ctabptr]; - if (dcb->version >= 0x30) - dcb->i2c_default_indices = dcb->i2c_table[4]; - - /* - * Parse the "management" I2C bus, used for hardware - * monitoring and some external TMDS transmitters. - */ - if (dcb->version >= 0x22) { - int idx = (dcb->version >= 0x40 ? - dcb->i2c_default_indices & 0xf : 2); - - read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, - idx, &dcb->i2c[idx]); - } } dcb_outp_foreach(dev, NULL, parse_dcb_entry); @@ -6893,19 +6756,6 @@ nouveau_run_vbios_init(struct drm_device *dev) return ret; } -static void -nouveau_bios_i2c_devices_takedown(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - struct dcb_i2c_entry *entry; - int i; - - entry = &bios->dcb.i2c[0]; - for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++) - nouveau_i2c_fini(dev, entry); -} - static bool nouveau_bios_posted(struct drm_device *dev) { @@ -6942,6 +6792,10 @@ nouveau_bios_init(struct drm_device *dev) if (ret) return ret; + ret = nouveau_i2c_init(dev); + if (ret) + return ret; + ret = parse_dcb_table(dev, bios); if (ret) return ret; @@ -6984,5 +6838,5 @@ nouveau_bios_init(struct drm_device *dev) void nouveau_bios_takedown(struct drm_device *dev) { - nouveau_bios_i2c_devices_takedown(dev); + nouveau_i2c_fini(dev); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index b9f2da394f89..a0e9c2c7ae8c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -53,13 +53,6 @@ struct bit_entry { int bit_table(struct drm_device *, u8 id, struct bit_entry *); -struct dcb_i2c_entry { - uint32_t entry; - uint8_t port_type; - uint8_t read, write; - struct nouveau_i2c_chan *chan; -}; - enum dcb_gpio_tag { DCB_GPIO_TVDAC0 = 0xc, DCB_GPIO_TVDAC1 = 0x2d, @@ -166,10 +159,6 @@ struct dcb_table { int entries; struct dcb_entry entry[DCB_MAX_NUM_ENTRIES]; - uint8_t *i2c_table; - uint8_t i2c_default_indices; - struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES]; - uint16_t gpio_table_ptr; struct dcb_gpio_table gpio; uint16_t connector_table_ptr; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 446caedbcff9..fb126c1508b5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -793,6 +793,7 @@ struct drm_nouveau_private { struct nouveau_vm *chan_vm; struct nvbios vbios; + struct list_head i2c_ports; struct nv04_mode_state mode_reg; struct nv04_mode_state saved_reg; diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index d39b2202b197..36ffcb84f55a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -109,13 +109,6 @@ 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) { @@ -125,7 +118,6 @@ nv50_i2c_getscl(void *data) return !!(nv_rd32(dev, i2c->rd) & 1); } - static int nv50_i2c_getsda(void *data) { @@ -166,125 +158,233 @@ nvd0_i2c_getsda(void *data) return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20); } +static const uint32_t nv50_i2c_port[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 +}; + +static u8 * +i2c_table(struct drm_device *dev, u8 *version) +{ + u8 *dcb = dcb_table(dev), *i2c = NULL; + if (dcb) { + if (dcb[0] >= 0x15) + i2c = ROMPTR(dev, dcb[2]); + if (dcb[0] >= 0x30) + i2c = ROMPTR(dev, dcb[4]); + } + + /* early revisions had no version number, use dcb version */ + if (i2c) { + *version = dcb[0]; + if (*version >= 0x30) + *version = i2c[0]; + } + + return i2c; +} + int -nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) +nouveau_i2c_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_i2c_chan *i2c; - int ret; - - if (entry->chan) - return -EEXIST; + struct nvbios *bios = &dev_priv->vbios; + struct nouveau_i2c_chan *port; + u8 *i2c, *entry, legacy[2][4] = {}; + u8 version, entries, recordlen; + int ret, i; + + INIT_LIST_HEAD(&dev_priv->i2c_ports); + + i2c = i2c_table(dev, &version); + if (!i2c) { + u8 *bmp = &bios->data[bios->offset]; + if (bios->type != NVBIOS_BMP) + return -ENODEV; + + legacy[0][0] = NV_CIO_CRE_DDC_WR__INDEX; + legacy[0][1] = NV_CIO_CRE_DDC_STATUS__INDEX; + legacy[1][0] = NV_CIO_CRE_DDC0_WR__INDEX; + legacy[1][1] = NV_CIO_CRE_DDC0_STATUS__INDEX; + + /* BMP (from v4.0) has i2c info in the structure, it's in a + * fixed location on earlier VBIOS + */ + if (bmp[5] < 4) + i2c = &bios->data[0x48]; + else + i2c = &bmp[0x36]; + + if (i2c[4]) legacy[0][0] = i2c[4]; + if (i2c[5]) legacy[0][1] = i2c[5]; + if (i2c[6]) legacy[1][0] = i2c[6]; + if (i2c[7]) legacy[1][1] = i2c[7]; + } - 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; + if (i2c && version >= 0x30) { + entry = i2c[1] + i2c; + entries = i2c[2]; + recordlen = i2c[3]; + } else + if (i2c) { + entry = i2c; + entries = 16; + recordlen = 4; + } else { + entry = legacy[0]; + entries = 2; + recordlen = 4; } - i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); - if (i2c == NULL) - return -ENOMEM; - - switch (entry->port_type) { - case 0: - i2c->bit.setsda = nv04_i2c_setsda; - i2c->bit.setscl = nv04_i2c_setscl; - i2c->bit.getsda = nv04_i2c_getsda; - i2c->bit.getscl = nv04_i2c_getscl; - i2c->rd = entry->read; - i2c->wr = entry->write; - break; - case 4: - i2c->bit.setsda = nv4e_i2c_setsda; - i2c->bit.setscl = nv4e_i2c_setscl; - i2c->bit.getsda = nv4e_i2c_getsda; - i2c->bit.getscl = nv4e_i2c_getscl; - i2c->rd = 0x600800 + entry->read; - i2c->wr = 0x600800 + entry->write; - break; - case 5: - i2c->bit.setsda = nv50_i2c_setsda; - i2c->bit.setscl = nv50_i2c_setscl; - 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; + for (i = 0; i < entries; i++, entry += recordlen) { + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (port == NULL) { + nouveau_i2c_fini(dev); + return -ENOMEM; + } + + port->type = entry[3]; + if (version < 0x30) { + port->type &= 0x07; + if (port->type == 0x07) + port->type = 0xff; + } + + if (port->type == 0xff) { + kfree(port); + continue; + } + + switch (port->type) { + case 0: /* NV04:NV50 */ + port->wr = entry[0]; + port->rd = entry[1]; + port->bit.setsda = nv04_i2c_setsda; + port->bit.setscl = nv04_i2c_setscl; + port->bit.getsda = nv04_i2c_getsda; + port->bit.getscl = nv04_i2c_getscl; + break; + case 4: /* NV4E */ + port->wr = 0x600800 + entry[1]; + port->rd = port->wr; + port->bit.setsda = nv4e_i2c_setsda; + port->bit.setscl = nv4e_i2c_setscl; + port->bit.getsda = nv4e_i2c_getsda; + port->bit.getscl = nv4e_i2c_getscl; + break; + case 5: /* NV50- */ + port->wr = entry[0] & 0x0f; + if (dev_priv->card_type < NV_D0) { + if (port->wr >= ARRAY_SIZE(nv50_i2c_port)) + break; + port->wr = nv50_i2c_port[port->wr]; + port->rd = port->wr; + port->bit.getsda = nv50_i2c_getsda; + port->bit.getscl = nv50_i2c_getscl; + } else { + port->wr = 0x00d014 + (port->wr * 0x20); + port->rd = port->wr; + port->bit.getsda = nvd0_i2c_getsda; + port->bit.getscl = nvd0_i2c_getscl; + } + port->bit.setsda = nv50_i2c_setsda; + port->bit.setscl = nv50_i2c_setscl; + break; + case 6: /* NV50- DP AUX */ + port->wr = entry[0]; + port->rd = port->wr; + port->adapter.algo = &nouveau_dp_i2c_algo; + break; + default: + break; + } + + if (!port->adapter.algo && !port->wr) { + NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", + i, port->type, port->wr, port->rd); + kfree(port); + continue; + } + + snprintf(port->adapter.name, sizeof(port->adapter.name), + "nouveau-%s-%d", pci_name(dev->pdev), i); + port->adapter.owner = THIS_MODULE; + port->adapter.dev.parent = &dev->pdev->dev; + port->dev = dev; + port->index = i; + port->dcb = ROM32(entry[0]); + i2c_set_adapdata(&port->adapter, i2c); + + if (port->adapter.algo != &nouveau_dp_i2c_algo) { + port->adapter.algo_data = &port->bit; + port->bit.udelay = 40; + port->bit.timeout = usecs_to_jiffies(5000); + port->bit.data = port; + ret = i2c_bit_add_bus(&port->adapter); } else { - i2c->bit.getsda = nvd0_i2c_getsda; - i2c->bit.getscl = nvd0_i2c_getscl; - i2c->rd = 0x00d014 + (entry->read * 0x20); - i2c->wr = i2c->rd; + port->adapter.algo = &nouveau_dp_i2c_algo; + ret = i2c_add_adapter(&port->adapter); } - break; - case 6: - i2c->rd = entry->read; - i2c->wr = entry->write; - break; - default: - NV_ERROR(dev, "DCB I2C port type %d unknown\n", - entry->port_type); - kfree(i2c); - return -EINVAL; - } - snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), - "nouveau-%s-%d", pci_name(dev->pdev), index); - i2c->adapter.owner = THIS_MODULE; - i2c->adapter.dev.parent = &dev->pdev->dev; - i2c->dev = dev; - i2c_set_adapdata(&i2c->adapter, i2c); - - if (entry->port_type < 6) { - i2c->adapter.algo_data = &i2c->bit; - i2c->bit.udelay = 40; - i2c->bit.timeout = usecs_to_jiffies(5000); - i2c->bit.data = i2c; - ret = i2c_bit_add_bus(&i2c->adapter); - } else { - i2c->adapter.algo = &nouveau_dp_i2c_algo; - ret = i2c_add_adapter(&i2c->adapter); - } + if (ret) { + NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); + kfree(port); + continue; + } - if (ret) { - NV_ERROR(dev, "Failed to register i2c %d\n", index); - kfree(i2c); - return ret; + list_add_tail(&port->head, &dev_priv->i2c_ports); } - entry->chan = i2c; return 0; } void -nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry) +nouveau_i2c_fini(struct drm_device *dev) { - if (!entry->chan) - return; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_i2c_chan *port, *tmp; - i2c_del_adapter(&entry->chan->adapter); - kfree(entry->chan); - entry->chan = NULL; + list_for_each_entry_safe(port, tmp, &dev_priv->i2c_ports, head) { + i2c_del_adapter(&port->adapter); + kfree(port); + } } struct nouveau_i2c_chan * -nouveau_i2c_find(struct drm_device *dev, int index) +nouveau_i2c_find(struct drm_device *dev, u8 index) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_i2c_entry *i2c = &dev_priv->vbios.dcb.i2c[index]; + struct nouveau_i2c_chan *port; + + if (index == NV_I2C_DEFAULT(0) || + index == NV_I2C_DEFAULT(1)) { + u8 version, *i2c = i2c_table(dev, &version); + if (i2c && version >= 0x30) { + if (index == NV_I2C_DEFAULT(0)) + index = (i2c[4] & 0x0f); + else + index = (i2c[4] & 0xf0) >> 4; + } else { + index = 2; + } + } - if (index >= DCB_MAX_NUM_I2C_ENTRIES) - return NULL; + list_for_each_entry(port, &dev_priv->i2c_ports, head) { + if (port->index == index) + break; + } - if (dev_priv->card_type >= NV_50 && (i2c->entry & 0x00000100)) { - uint32_t reg = 0xe500, val; + if (&port->head == &dev_priv->i2c_ports) + return NULL; - if (i2c->port_type == 6) { - reg += i2c->read * 0x50; + if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) { + u32 reg = 0x00e500, val; + if (port->type == 6) { + reg += port->rd * 0x50; val = 0x2002; } else { - reg += ((i2c->entry & 0x1e00) >> 9) * 0x50; + reg += ((port->dcb & 0x1e00) >> 9) * 0x50; val = 0xe001; } @@ -294,9 +394,7 @@ nouveau_i2c_find(struct drm_device *dev, int index) nv_mask(dev, reg + 0x00, 0x0000f003, val); } - if (!i2c->chan && nouveau_i2c_init(dev, i2c, index)) - return NULL; - return i2c->chan; + return port; } bool diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index 422b62fd8272..cf5f67d51fba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -27,20 +27,26 @@ #include #include "drm_dp_helper.h" -struct dcb_i2c_entry; +#define NV_I2C_PORT(n) (0x00 + (n)) +#define NV_I2C_PORT_NUM 0x10 +#define NV_I2C_DEFAULT(n) (0x80 + (n)) struct nouveau_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; struct i2c_algo_bit_data bit; + struct list_head head; + u8 index; + u8 type; + u32 dcb; unsigned rd; unsigned wr; unsigned data; }; -int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index); -void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *); -struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index); +int nouveau_i2c_init(struct drm_device *); +void nouveau_i2c_fini(struct drm_device *); +struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, u8 index); bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr); int nouveau_i2c_identify(struct drm_device *dev, const char *what, struct i2c_board_info *info, diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 4c46adeb0715..0f5a30160556 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -286,8 +286,6 @@ probe_monitoring_device(struct nouveau_i2c_chan *i2c, static void nouveau_temp_probe_i2c(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_table *dcb = &dev_priv->vbios.dcb; struct i2c_board_info info[] = { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, { I2C_BOARD_INFO("w83781d", 0x2d) }, @@ -296,11 +294,9 @@ nouveau_temp_probe_i2c(struct drm_device *dev) { I2C_BOARD_INFO("lm99", 0x4c) }, { } }; - int idx = (dcb->version >= 0x40 ? - dcb->i2c_default_indices & 0xf : 2); nouveau_i2c_identify(dev, "monitoring device", info, - probe_monitoring_device, idx); + probe_monitoring_device, NV_I2C_DEFAULT(0)); } void -- cgit v1.2.3 From 2bdb06e3cff066c546fb41152bc582a5ec73e899 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 17 Nov 2011 13:56:14 +1000 Subject: drm/nouveau/i2c: tidy up bit-bang helpers, also fixing nv50 setsda bug Was using nv_mask, which is bad. Reading the reg senses the current line states, which aren't necessarily the states we're trying to drive the lines to. Fixed to store SCL driver state just as we already do for SDA. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 6 +- drivers/gpu/drm/nouveau/nouveau_i2c.c | 230 +++++++++++++--------------------- drivers/gpu/drm/nouveau/nouveau_i2c.h | 6 +- 3 files changed, 94 insertions(+), 148 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 5722fd55764b..07bac3602453 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -578,7 +578,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) dp.dcb = nv_encoder->dcb; dp.crtc = nv_crtc->index; - dp.auxch = auxch->rd; + dp.auxch = auxch->drive; dp.or = nv_encoder->or; dp.link = !(nv_encoder->dcb->sorconf.link & 1); dp.dpcd = nv_encoder->dp.dpcd; @@ -653,7 +653,7 @@ nouveau_dp_detect(struct drm_encoder *encoder) if (!auxch) return false; - ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8); + ret = auxch_tx(dev, auxch->drive, 9, DP_DPCD_REV, dpcd, 8); if (ret) return false; @@ -681,7 +681,7 @@ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr) { - return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr); + return auxch_tx(auxch->dev, auxch->drive, cmd, addr, data, data_nr); } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index 36ffcb84f55a..d0d581440d5e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -30,132 +30,83 @@ #include "nouveau_hw.h" static void -nv04_i2c_setscl(void *data, int state) +i2c_drive_scl(void *data, int state) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0); - NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); -} - -static void -nv04_i2c_setsda(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0); - NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); -} - -static int -nv04_i2c_getscl(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4); -} - -static int -nv04_i2c_getsda(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8); -} - -static void -nv4e_i2c_setscl(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0); - nv_wr32(dev, i2c->wr, val | 0x01); -} - -static void -nv4e_i2c_setsda(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0); - nv_wr32(dev, i2c->wr, val | 0x01); -} - -static int -nv4e_i2c_getscl(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!((nv_rd32(dev, i2c->rd) >> 16) & 4); -} - -static int -nv4e_i2c_getsda(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); -} - -static int -nv50_i2c_getscl(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(nv_rd32(dev, i2c->rd) & 1); -} - -static int -nv50_i2c_getsda(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(nv_rd32(dev, i2c->rd) & 2); -} - -static void -nv50_i2c_setscl(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - - nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); + struct nouveau_i2c_chan *port = data; + if (port->type == 0) { + u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); + if (state) val |= 0x20; + else val &= 0xdf; + NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); + } else + if (port->type == 4) { + nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01); + } else + if (port->type == 5) { + if (state) port->state |= 0x01; + else port->state &= 0xfe; + nv_wr32(port->dev, port->drive, 4 | port->state); + } } static void -nv50_i2c_setsda(void *data, int state) +i2c_drive_sda(void *data, int state) { - struct nouveau_i2c_chan *i2c = data; - - nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0)); - i2c->data = state; + struct nouveau_i2c_chan *port = data; + if (port->type == 0) { + u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); + if (state) val |= 0x10; + else val &= 0xef; + NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); + } else + if (port->type == 4) { + nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01); + } else + if (port->type == 5) { + if (state) port->state |= 0x02; + else port->state &= 0xfd; + nv_wr32(port->dev, port->drive, 4 | port->state); + } } static int -nvd0_i2c_getscl(void *data) +i2c_sense_scl(void *data) { - struct nouveau_i2c_chan *i2c = data; - return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10); + struct nouveau_i2c_chan *port = data; + struct drm_nouveau_private *dev_priv = port->dev->dev_private; + if (port->type == 0) { + return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x04); + } else + if (port->type == 4) { + return !!(nv_rd32(port->dev, port->sense) & 0x00040000); + } else + if (port->type == 5) { + if (dev_priv->card_type < NV_D0) + return !!(nv_rd32(port->dev, port->sense) & 0x01); + else + return !!(nv_rd32(port->dev, port->sense) & 0x10); + } + return 0; } static int -nvd0_i2c_getsda(void *data) +i2c_sense_sda(void *data) { - struct nouveau_i2c_chan *i2c = data; - return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20); + struct nouveau_i2c_chan *port = data; + struct drm_nouveau_private *dev_priv = port->dev->dev_private; + if (port->type == 0) { + return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x08); + } else + if (port->type == 4) { + return !!(nv_rd32(port->dev, port->sense) & 0x00080000); + } else + if (port->type == 5) { + if (dev_priv->card_type < NV_D0) + return !!(nv_rd32(port->dev, port->sense) & 0x02); + else + return !!(nv_rd32(port->dev, port->sense) & 0x20); + } + return 0; } static const uint32_t nv50_i2c_port[] = { @@ -258,51 +209,37 @@ nouveau_i2c_init(struct drm_device *dev) switch (port->type) { case 0: /* NV04:NV50 */ - port->wr = entry[0]; - port->rd = entry[1]; - port->bit.setsda = nv04_i2c_setsda; - port->bit.setscl = nv04_i2c_setscl; - port->bit.getsda = nv04_i2c_getsda; - port->bit.getscl = nv04_i2c_getscl; + port->drive = entry[0]; + port->sense = entry[1]; break; case 4: /* NV4E */ - port->wr = 0x600800 + entry[1]; - port->rd = port->wr; - port->bit.setsda = nv4e_i2c_setsda; - port->bit.setscl = nv4e_i2c_setscl; - port->bit.getsda = nv4e_i2c_getsda; - port->bit.getscl = nv4e_i2c_getscl; + port->drive = 0x600800 + entry[1]; + port->sense = port->drive; break; case 5: /* NV50- */ - port->wr = entry[0] & 0x0f; + port->drive = entry[0] & 0x0f; if (dev_priv->card_type < NV_D0) { - if (port->wr >= ARRAY_SIZE(nv50_i2c_port)) + if (port->drive >= ARRAY_SIZE(nv50_i2c_port)) break; - port->wr = nv50_i2c_port[port->wr]; - port->rd = port->wr; - port->bit.getsda = nv50_i2c_getsda; - port->bit.getscl = nv50_i2c_getscl; + port->drive = nv50_i2c_port[port->drive]; + port->sense = port->drive; } else { - port->wr = 0x00d014 + (port->wr * 0x20); - port->rd = port->wr; - port->bit.getsda = nvd0_i2c_getsda; - port->bit.getscl = nvd0_i2c_getscl; + port->drive = 0x00d014 + (port->drive * 0x20); + port->sense = port->drive; } - port->bit.setsda = nv50_i2c_setsda; - port->bit.setscl = nv50_i2c_setscl; break; case 6: /* NV50- DP AUX */ - port->wr = entry[0]; - port->rd = port->wr; + port->drive = entry[0]; + port->sense = port->drive; port->adapter.algo = &nouveau_dp_i2c_algo; break; default: break; } - if (!port->adapter.algo && !port->wr) { + if (!port->adapter.algo && !port->drive) { NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", - i, port->type, port->wr, port->rd); + i, port->type, port->drive, port->sense); kfree(port); continue; } @@ -321,6 +258,15 @@ nouveau_i2c_init(struct drm_device *dev) port->bit.udelay = 40; port->bit.timeout = usecs_to_jiffies(5000); port->bit.data = port; + port->bit.setsda = i2c_drive_sda; + port->bit.setscl = i2c_drive_scl; + port->bit.getsda = i2c_sense_sda; + port->bit.getscl = i2c_sense_scl; + + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + i2c_drive_scl(port, 1); + ret = i2c_bit_add_bus(&port->adapter); } else { port->adapter.algo = &nouveau_dp_i2c_algo; @@ -381,7 +327,7 @@ nouveau_i2c_find(struct drm_device *dev, u8 index) if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) { u32 reg = 0x00e500, val; if (port->type == 6) { - reg += port->rd * 0x50; + reg += port->drive * 0x50; val = 0x2002; } else { reg += ((port->dcb & 0x1e00) >> 9) * 0x50; diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index cf5f67d51fba..1d083893a4d7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -39,9 +39,9 @@ struct nouveau_i2c_chan { u8 index; u8 type; u32 dcb; - unsigned rd; - unsigned wr; - unsigned data; + u32 drive; + u32 sense; + u32 state; }; int nouveau_i2c_init(struct drm_device *); -- cgit v1.2.3 From 9e3b6b99075a01ce47379eee74365aece14242f3 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 22 Nov 2011 22:36:18 +1000 Subject: drm/nouveau/i2c: fix debug message Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_i2c.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index d0d581440d5e..116ef3193fc7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -375,9 +375,13 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what, struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index); int i; - NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index); + if (!i2c) { + NV_DEBUG(dev, "No bus when probing %s on %d\n", what, index); + return -ENODEV; + } - for (i = 0; i2c && info[i].addr; i++) { + NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, i2c->index); + for (i = 0; info[i].addr; i++) { if (nouveau_probe_i2c_addr(i2c, info[i].addr) && (!match || match(i2c, &info[i]))) { NV_INFO(dev, "Detected %s: %s\n", what, info[i].type); @@ -386,6 +390,5 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what, } NV_DEBUG(dev, "No devices found.\n"); - return -ENODEV; } -- cgit v1.2.3 From f553b79c03f0dbd52f6f03abe8233a2bef8cbd0d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 21 Dec 2011 18:09:12 +1000 Subject: drm/nouveau/i2c: handle bit-banging ourselves i2c-algo-bit doesn't actually work very well on one card I have access to (NVS 300), random single-bit errors occur most of the time - what we're doing now is closer to what xf86i2c.c does. The original plan was to figure out why i2c-algo-bit fails on the NVS 300, and fix it. However, while investigating I discovered i2c-algo-bit calls cond_resched(), which makes it a bad idea for us to be using as we execute VBIOS scripts from a tasklet, and there may very well be i2c transfers as a result. So, since I already wrote this code in userspace to track down the NVS 300 bug, and it's not really much code - lets use it. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_i2c.c | 199 ++++++++++++++++++++++++++++++---- drivers/gpu/drm/nouveau/nouveau_i2c.h | 1 - 2 files changed, 178 insertions(+), 22 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index 116ef3193fc7..820ae7f52044 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -29,6 +29,10 @@ #include "nouveau_i2c.h" #include "nouveau_hw.h" +#define T_TIMEOUT 2200000 +#define T_RISEFALL 1000 +#define T_HOLD 5000 + static void i2c_drive_scl(void *data, int state) { @@ -109,6 +113,175 @@ i2c_sense_sda(void *data) return 0; } +static void +i2c_delay(struct nouveau_i2c_chan *port, u32 nsec) +{ + udelay((nsec + 500) / 1000); +} + +static bool +i2c_raise_scl(struct nouveau_i2c_chan *port) +{ + u32 timeout = T_TIMEOUT / T_RISEFALL; + + i2c_drive_scl(port, 1); + do { + i2c_delay(port, T_RISEFALL); + } while (!i2c_sense_scl(port) && --timeout); + + return timeout != 0; +} + +static int +i2c_start(struct nouveau_i2c_chan *port) +{ + int ret = 0; + + port->state = i2c_sense_scl(port); + port->state |= i2c_sense_sda(port) << 1; + if (port->state != 3) { + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + if (!i2c_raise_scl(port)) + ret = -EBUSY; + } + + i2c_drive_sda(port, 0); + i2c_delay(port, T_HOLD); + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return ret; +} + +static void +i2c_stop(struct nouveau_i2c_chan *port) +{ + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 0); + i2c_delay(port, T_RISEFALL); + + i2c_drive_scl(port, 1); + i2c_delay(port, T_HOLD); + i2c_drive_sda(port, 1); + i2c_delay(port, T_HOLD); +} + +static int +i2c_bitw(struct nouveau_i2c_chan *port, int sda) +{ + i2c_drive_sda(port, sda); + i2c_delay(port, T_RISEFALL); + + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return 0; +} + +static int +i2c_bitr(struct nouveau_i2c_chan *port) +{ + int sda; + + i2c_drive_sda(port, 1); + i2c_delay(port, T_RISEFALL); + + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + + sda = i2c_sense_sda(port); + + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return sda; +} + +static int +i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last) +{ + int i, bit; + + *byte = 0; + for (i = 7; i >= 0; i--) { + bit = i2c_bitr(port); + if (bit < 0) + return bit; + *byte |= bit << i; + } + + return i2c_bitw(port, last ? 1 : 0); +} + +static int +i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte) +{ + int i, ret; + for (i = 7; i >= 0; i--) { + ret = i2c_bitw(port, !!(byte & (1 << i))); + if (ret < 0) + return ret; + } + + ret = i2c_bitr(port); + if (ret == 1) /* nack */ + ret = -EIO; + return ret; +} + +static int +i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg) +{ + u32 addr = msg->addr << 1; + if (msg->flags & I2C_M_RD) + addr |= 1; + return i2c_put_byte(port, addr); +} + +static int +i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap; + struct i2c_msg *msg = msgs; + int ret = 0, mcnt = num; + + while (!ret && mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; + + ret = i2c_start(port); + if (ret == 0) + ret = i2c_addr(port, msg); + + if (msg->flags & I2C_M_RD) { + while (!ret && remaining--) + ret = i2c_get_byte(port, ptr++, !remaining); + } else { + while (!ret && remaining--) + ret = i2c_put_byte(port, *ptr++); + } + + msg++; + } + + i2c_stop(port); + return (ret < 0) ? ret : num; +} + +static u32 +i2c_bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +const struct i2c_algorithm i2c_bit_algo = { + .master_xfer = i2c_bit_xfer, + .functionality = i2c_bit_func +}; + static const uint32_t nv50_i2c_port[] = { 0x00e138, 0x00e150, 0x00e168, 0x00e180, 0x00e254, 0x00e274, 0x00e764, 0x00e780, @@ -211,10 +384,12 @@ nouveau_i2c_init(struct drm_device *dev) case 0: /* NV04:NV50 */ port->drive = entry[0]; port->sense = entry[1]; + port->adapter.algo = &i2c_bit_algo; break; case 4: /* NV4E */ port->drive = 0x600800 + entry[1]; port->sense = port->drive; + port->adapter.algo = &i2c_bit_algo; break; case 5: /* NV50- */ port->drive = entry[0] & 0x0f; @@ -227,6 +402,7 @@ nouveau_i2c_init(struct drm_device *dev) port->drive = 0x00d014 + (port->drive * 0x20); port->sense = port->drive; } + port->adapter.algo = &i2c_bit_algo; break; case 6: /* NV50- DP AUX */ port->drive = entry[0]; @@ -237,7 +413,7 @@ nouveau_i2c_init(struct drm_device *dev) break; } - if (!port->adapter.algo && !port->drive) { + if (!port->adapter.algo) { NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", i, port->type, port->drive, port->sense); kfree(port); @@ -253,26 +429,7 @@ nouveau_i2c_init(struct drm_device *dev) port->dcb = ROM32(entry[0]); i2c_set_adapdata(&port->adapter, i2c); - if (port->adapter.algo != &nouveau_dp_i2c_algo) { - port->adapter.algo_data = &port->bit; - port->bit.udelay = 40; - port->bit.timeout = usecs_to_jiffies(5000); - port->bit.data = port; - port->bit.setsda = i2c_drive_sda; - port->bit.setscl = i2c_drive_scl; - port->bit.getsda = i2c_sense_sda; - port->bit.getscl = i2c_sense_scl; - - i2c_drive_scl(port, 0); - i2c_drive_sda(port, 1); - i2c_drive_scl(port, 1); - - ret = i2c_bit_add_bus(&port->adapter); - } else { - port->adapter.algo = &nouveau_dp_i2c_algo; - ret = i2c_add_adapter(&port->adapter); - } - + ret = i2c_add_adapter(&port->adapter); if (ret) { NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); kfree(port); diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index 1d083893a4d7..4d2e4e9031be 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -34,7 +34,6 @@ struct nouveau_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; - struct i2c_algo_bit_data bit; struct list_head head; u8 index; u8 type; -- cgit v1.2.3 From befb51e9c97e783c86a1da27bdda3a638d2f02c7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 18 Nov 2011 10:23:59 +1000 Subject: drm/nouveau/disp: parse connector info directly in nouveau_connector.c Another case where we parsed vbios data to some structs, then again use that info once to construct another set of data. Skip the intermediate step. This is also slightly improved in that we can now use DCB 3.x connector table info, which will allow NV4x to gain hotplug support, and to make quirks for SPWG LVDS panels unnecessary. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 315 ++++++++-------------------- drivers/gpu/drm/nouveau/nouveau_bios.h | 19 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 216 ++++++++++++------- drivers/gpu/drm/nouveau/nouveau_connector.h | 6 +- drivers/gpu/drm/nouveau/nouveau_dp.c | 4 +- drivers/gpu/drm/nouveau/nv50_display.c | 6 +- drivers/gpu/drm/nouveau/nvd0_display.c | 2 +- 7 files changed, 240 insertions(+), 328 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 7922bb969d25..35b18f4f7c4e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1100,13 +1100,9 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) switch (cond) { case 0: - { - struct dcb_connector_table_entry *ent = - &bios->dcb.connector.entry[dcb->connector]; - - if (ent->type != DCB_CONNECTOR_eDP) + entry = dcb_conn(dev, dcb->connector); + if (!entry || entry[0] != DCB_CONNECTOR_eDP) iexec->execute = false; - } break; case 1: case 2: @@ -5782,164 +5778,6 @@ no_table: } } -struct dcb_connector_table_entry * -nouveau_bios_connector_entry(struct drm_device *dev, int index) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - struct dcb_connector_table_entry *cte; - - if (index >= bios->dcb.connector.entries) - return NULL; - - cte = &bios->dcb.connector.entry[index]; - if (cte->type == 0xff) - return NULL; - - return cte; -} - -static enum dcb_connector_type -divine_connector_type(struct nvbios *bios, int index) -{ - struct dcb_table *dcb = &bios->dcb; - unsigned encoders = 0, type = DCB_CONNECTOR_NONE; - int i; - - for (i = 0; i < dcb->entries; i++) { - if (dcb->entry[i].connector == index) - encoders |= (1 << dcb->entry[i].type); - } - - if (encoders & (1 << OUTPUT_DP)) { - if (encoders & (1 << OUTPUT_TMDS)) - type = DCB_CONNECTOR_DP; - else - type = DCB_CONNECTOR_eDP; - } else - if (encoders & (1 << OUTPUT_TMDS)) { - if (encoders & (1 << OUTPUT_ANALOG)) - type = DCB_CONNECTOR_DVI_I; - else - type = DCB_CONNECTOR_DVI_D; - } else - if (encoders & (1 << OUTPUT_ANALOG)) { - type = DCB_CONNECTOR_VGA; - } else - if (encoders & (1 << OUTPUT_LVDS)) { - type = DCB_CONNECTOR_LVDS; - } else - if (encoders & (1 << OUTPUT_TV)) { - type = DCB_CONNECTOR_TV_0; - } - - return type; -} - -static void -apply_dcb_connector_quirks(struct nvbios *bios, int idx) -{ - struct dcb_connector_table_entry *cte = &bios->dcb.connector.entry[idx]; - struct drm_device *dev = bios->dev; - - /* Gigabyte NX85T */ - if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { - if (cte->type == DCB_CONNECTOR_HDMI_1) - cte->type = DCB_CONNECTOR_DVI_I; - } - - /* Gigabyte GV-NX86T512H */ - if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { - if (cte->type == DCB_CONNECTOR_HDMI_1) - cte->type = DCB_CONNECTOR_DVI_I; - } -} - -static const u8 hpd_gpio[16] = { - 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60, -}; - -static void -parse_dcb_connector_table(struct nvbios *bios) -{ - struct drm_device *dev = bios->dev; - struct dcb_connector_table *ct = &bios->dcb.connector; - struct dcb_connector_table_entry *cte; - uint8_t *conntab = &bios->data[bios->dcb.connector_table_ptr]; - uint8_t *entry; - int i; - - if (!bios->dcb.connector_table_ptr) { - NV_DEBUG_KMS(dev, "No DCB connector table present\n"); - return; - } - - NV_INFO(dev, "DCB connector table: VHER 0x%02x %d %d %d\n", - conntab[0], conntab[1], conntab[2], conntab[3]); - if ((conntab[0] != 0x30 && conntab[0] != 0x40) || - (conntab[3] != 2 && conntab[3] != 4)) { - NV_ERROR(dev, " Unknown! Please report.\n"); - return; - } - - ct->entries = conntab[2]; - - entry = conntab + conntab[1]; - cte = &ct->entry[0]; - for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) { - cte->index = i; - if (conntab[3] == 2) - cte->entry = ROM16(entry[0]); - else - cte->entry = ROM32(entry[0]); - - cte->type = (cte->entry & 0x000000ff) >> 0; - cte->index2 = (cte->entry & 0x00000f00) >> 8; - - cte->gpio_tag = ffs((cte->entry & 0x07033000) >> 12); - cte->gpio_tag = hpd_gpio[cte->gpio_tag]; - - if (cte->type == 0xff) - continue; - - apply_dcb_connector_quirks(bios, i); - - NV_INFO(dev, " %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n", - i, cte->entry, cte->type, cte->index, cte->gpio_tag); - - /* check for known types, fallback to guessing the type - * from attached encoders if we hit an unknown. - */ - switch (cte->type) { - case DCB_CONNECTOR_VGA: - case DCB_CONNECTOR_TV_0: - case DCB_CONNECTOR_TV_1: - case DCB_CONNECTOR_TV_3: - case DCB_CONNECTOR_DVI_I: - case DCB_CONNECTOR_DVI_D: - case DCB_CONNECTOR_LVDS: - case DCB_CONNECTOR_LVDS_SPWG: - case DCB_CONNECTOR_DP: - case DCB_CONNECTOR_eDP: - case DCB_CONNECTOR_HDMI_0: - case DCB_CONNECTOR_HDMI_1: - break; - default: - cte->type = divine_connector_type(bios, cte->index); - NV_WARN(dev, "unknown type, using 0x%02x\n", cte->type); - break; - } - - if (nouveau_override_conntype) { - int type = divine_connector_type(bios, cte->index); - if (type != cte->type) - NV_WARN(dev, " -> type 0x%02x\n", cte->type); - } - - } -} - void * dcb_table(struct drm_device *dev) { @@ -6043,6 +5881,27 @@ dcb_outp_foreach(struct drm_device *dev, void *data, return 0; } +u8 * +dcb_conntab(struct drm_device *dev) +{ + u8 *dcb = dcb_table(dev); + if (dcb && dcb[0] >= 0x30 && dcb[1] >= 0x16) { + u8 *conntab = ROMPTR(dev, dcb[0x14]); + if (conntab && conntab[0] >= 0x30 && conntab[0] <= 0x40) + return conntab; + } + return NULL; +} + +u8 * +dcb_conn(struct drm_device *dev, u8 idx) +{ + u8 *conntab = dcb_conntab(dev); + if (conntab && idx < conntab[2]) + return conntab + conntab[1] + (idx * conntab[3]); + return NULL; +} + static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb) { struct dcb_entry *entry = &dcb->entry[dcb->entries]; @@ -6073,8 +5932,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, entry->type = conn & 0xf; entry->i2c_index = (conn >> 4) & 0xf; entry->heads = (conn >> 8) & 0xf; - if (dcb->version >= 0x40) - entry->connector = (conn >> 12) & 0xf; + entry->connector = (conn >> 12) & 0xf; entry->bus = (conn >> 16) & 0xf; entry->location = (conn >> 20) & 0x3; entry->or = (conn >> 24) & 0xf; @@ -6433,7 +6291,7 @@ parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp) if (apply_dcb_encoder_quirks(dev, idx, &conn, &conf)) { struct dcb_entry *entry = new_dcb_entry(dcb); - NV_TRACEWARN(dev, "DCB entry %02d: %08x %08x\n", idx, conn, conf); + NV_TRACEWARN(dev, "DCB outp %02d: %08x %08x\n", idx, conn, conf); if (dcb->version >= 0x20) ret = parse_dcb20_entry(dev, dcb, conn, conf, entry); @@ -6441,16 +6299,67 @@ parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp) ret = parse_dcb15_entry(dev, dcb, conn, conf, entry); if (!ret) return 1; /* stop parsing */ + + /* Ignore the I2C index for on-chip TV-out, as there + * are cards with bogus values (nv31m in bug 23212), + * and it's otherwise useless. + */ + if (entry->type == OUTPUT_TV && + entry->location == DCB_LOC_ON_CHIP) + entry->i2c_index = 0x0f; } return 0; } +static void +dcb_fake_connectors(struct nvbios *bios) +{ + struct dcb_table *dcbt = &bios->dcb; + u8 map[16] = { }; + int i, idx = 0; + + /* heuristic: if we ever get a non-zero connector field, assume + * that all the indices are valid and we don't need fake them. + */ + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector) + return; + } + + /* no useful connector info available, we need to make it up + * ourselves. the rule here is: anything on the same i2c bus + * is considered to be on the same connector. any output + * without an associated i2c bus is assigned its own unique + * connector index. + */ + for (i = 0; i < dcbt->entries; i++) { + u8 i2c = dcbt->entry[i].i2c_index; + if (i2c == 0x0f) { + dcbt->entry[i].connector = idx++; + } else { + if (!map[i2c]) + map[i2c] = ++idx; + dcbt->entry[i].connector = map[i2c] - 1; + } + } + + /* if we created more than one connector, destroy the connector + * table - just in case it has random, rather than stub, entries. + */ + if (i > 1) { + u8 *conntab = dcb_conntab(bios->dev); + if (conntab) + conntab[0] = 0x00; + } +} + static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios) { struct dcb_table *dcb = &bios->dcb; - u8 *dcbt; + u8 *dcbt, *conn; + int idx; dcbt = dcb_table(dev); if (!dcbt) { @@ -6466,10 +6375,8 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) NV_TRACE(dev, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf); dcb->version = dcbt[0]; - if (dcb->version >= 0x30) { + if (dcb->version >= 0x30) dcb->gpio_table_ptr = ROM16(dcbt[10]); - dcb->connector_table_ptr = ROM16(dcbt[20]); - } dcb_outp_foreach(dev, NULL, parse_dcb_entry); @@ -6483,61 +6390,21 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) if (!dcb->entries) return -ENXIO; - parse_dcb_gpio_table(bios); - parse_dcb_connector_table(bios); - return 0; -} - -static void -fixup_legacy_connector(struct nvbios *bios) -{ - struct dcb_table *dcb = &bios->dcb; - int i, i2c, i2c_conn[DCB_MAX_NUM_I2C_ENTRIES] = { }; - - /* - * DCB 3.0 also has the table in most cases, but there are some cards - * where the table is filled with stub entries, and the DCB entriy - * indices are all 0. We don't need the connector indices on pre-G80 - * chips (yet?) so limit the use to DCB 4.0 and above. - */ - if (dcb->version >= 0x40) - return; - - dcb->connector.entries = 0; - - /* - * No known connector info before v3.0, so make it up. the rule here - * is: anything on the same i2c bus is considered to be on the same - * connector. any output without an associated i2c bus is assigned - * its own unique connector index. - */ - for (i = 0; i < dcb->entries; i++) { - /* - * Ignore the I2C index for on-chip TV-out, as there - * are cards with bogus values (nv31m in bug 23212), - * and it's otherwise useless. - */ - if (dcb->entry[i].type == OUTPUT_TV && - dcb->entry[i].location == DCB_LOC_ON_CHIP) - dcb->entry[i].i2c_index = 0xf; - i2c = dcb->entry[i].i2c_index; - - if (i2c_conn[i2c]) { - dcb->entry[i].connector = i2c_conn[i2c] - 1; - continue; + /* dump connector table entries to log, if any exist */ + idx = -1; + while ((conn = dcb_conn(dev, ++idx))) { + if (conn[0] != 0xff) { + NV_TRACE(dev, "DCB conn %02d: ", idx); + if (dcb_conntab(dev)[3] < 4) + printk("%04x\n", ROM16(conn[0])); + else + printk("%08x\n", ROM32(conn[0])); } - - dcb->entry[i].connector = dcb->connector.entries++; - if (i2c != 0xf) - i2c_conn[i2c] = dcb->connector.entries; } + dcb_fake_connectors(bios); - /* Fake the connector table as well as just connector indices */ - for (i = 0; i < dcb->connector.entries; i++) { - dcb->connector.entry[i].index = i; - dcb->connector.entry[i].type = divine_connector_type(bios, i); - dcb->connector.entry[i].gpio_tag = 0xff; - } + parse_dcb_gpio_table(bios); + return 0; } static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry) @@ -6800,8 +6667,6 @@ nouveau_bios_init(struct drm_device *dev) if (ret) return ret; - fixup_legacy_connector(bios); - if (!bios->major_version) /* we don't run version 0 bios */ return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index a0e9c2c7ae8c..481e1f23f2f1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -58,6 +58,7 @@ enum dcb_gpio_tag { DCB_GPIO_TVDAC1 = 0x2d, DCB_GPIO_PWM_FAN = 0x9, DCB_GPIO_FAN_SENSE = 0x3d, + DCB_GPIO_UNUSED = 0xff }; struct dcb_gpio_entry { @@ -89,20 +90,6 @@ enum dcb_connector_type { DCB_CONNECTOR_NONE = 0xff }; -struct dcb_connector_table_entry { - uint8_t index; - uint32_t entry; - enum dcb_connector_type type; - uint8_t index2; - uint8_t gpio_tag; - void *drm; -}; - -struct dcb_connector_table { - int entries; - struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES]; -}; - enum dcb_type { OUTPUT_ANALOG = 0, OUTPUT_TV = 1, @@ -161,8 +148,6 @@ struct dcb_table { uint16_t gpio_table_ptr; struct dcb_gpio_table gpio; - uint16_t connector_table_ptr; - struct dcb_connector_table connector; }; enum nouveau_or { @@ -333,5 +318,7 @@ void *dcb_table(struct drm_device *); u8 *dcb_outp(struct drm_device *, u8 idx); int dcb_outp_foreach(struct drm_device *, void *data, int (*)(struct drm_device *, void *, int idx, u8 *outp)); +u8 *dcb_conntab(struct drm_device *); +u8 *dcb_conn(struct drm_device *, u8 idx); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 576fb9af5d13..f6c8e1da4228 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -112,7 +112,7 @@ nouveau_connector_destroy(struct drm_connector *connector) pgpio = &dev_priv->engine.gpio; if (pgpio->irq_unregister) { - pgpio->irq_unregister(dev, nv_connector->dcb->gpio_tag, + pgpio->irq_unregister(dev, nv_connector->hpd, nouveau_connector_hotplug, connector); } @@ -218,7 +218,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector, connector->interlace_allowed = true; } - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { drm_connector_property_set_value(connector, dev->mode_config.dvi_i_subconnector_property, nv_encoder->dcb->type == OUTPUT_TMDS ? @@ -401,7 +401,7 @@ nouveau_connector_force(struct drm_connector *connector) struct nouveau_encoder *nv_encoder; int type; - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { if (connector->force == DRM_FORCE_ON_DIGITAL) type = OUTPUT_TMDS; else @@ -675,7 +675,7 @@ nouveau_connector_detect_depth(struct drm_connector *connector) * know which if_is_24bit flag to check... */ if (nv_connector->edid && - nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) duallink = ((u8 *)nv_connector->edid)[121] == 2; else duallink = mode->clock >= bios->fp.duallink_transition_clk; @@ -738,9 +738,9 @@ nouveau_connector_get_modes(struct drm_connector *connector) if (nv_encoder->dcb->type == OUTPUT_TV) ret = get_slave_funcs(encoder)->get_modes(encoder, connector); - if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS || - nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG || - nv_connector->dcb->type == DCB_CONNECTOR_eDP) + if (nv_connector->type == DCB_CONNECTOR_LVDS || + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG || + nv_connector->type == DCB_CONNECTOR_eDP) ret += nouveau_connector_scaler_modes_add(connector); return ret; @@ -857,6 +857,29 @@ nouveau_connector_funcs_lvds = { .force = nouveau_connector_force }; +static int +drm_conntype_from_dcb(enum dcb_connector_type dcb) +{ + switch (dcb) { + case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA; + case DCB_CONNECTOR_TV_0 : + case DCB_CONNECTOR_TV_1 : + case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV; + case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII; + case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID; + case DCB_CONNECTOR_LVDS : + case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS; + case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort; + case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; + case DCB_CONNECTOR_HDMI_0 : + case DCB_CONNECTOR_HDMI_1 : return DRM_MODE_CONNECTOR_HDMIA; + default: + break; + } + + return DRM_MODE_CONNECTOR_Unknown; +} + struct drm_connector * nouveau_connector_create(struct drm_device *dev, int index) { @@ -865,90 +888,133 @@ nouveau_connector_create(struct drm_device *dev, int index) struct nouveau_display_engine *disp = &dev_priv->engine.display; struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_connector *nv_connector = NULL; - struct dcb_connector_table_entry *dcb = NULL; struct drm_connector *connector; int type, ret = 0; + bool dummy; NV_DEBUG_KMS(dev, "\n"); - if (index >= dev_priv->vbios.dcb.connector.entries) - return ERR_PTR(-EINVAL); - - dcb = &dev_priv->vbios.dcb.connector.entry[index]; - if (dcb->drm) - return dcb->drm; - - switch (dcb->type) { - case DCB_CONNECTOR_VGA: - type = DRM_MODE_CONNECTOR_VGA; - break; - case DCB_CONNECTOR_TV_0: - case DCB_CONNECTOR_TV_1: - case DCB_CONNECTOR_TV_3: - type = DRM_MODE_CONNECTOR_TV; - break; - case DCB_CONNECTOR_DVI_I: - type = DRM_MODE_CONNECTOR_DVII; - break; - case DCB_CONNECTOR_DVI_D: - type = DRM_MODE_CONNECTOR_DVID; - break; - case DCB_CONNECTOR_HDMI_0: - case DCB_CONNECTOR_HDMI_1: - type = DRM_MODE_CONNECTOR_HDMIA; - break; - case DCB_CONNECTOR_LVDS: - case DCB_CONNECTOR_LVDS_SPWG: - type = DRM_MODE_CONNECTOR_LVDS; - funcs = &nouveau_connector_funcs_lvds; - break; - case DCB_CONNECTOR_DP: - type = DRM_MODE_CONNECTOR_DisplayPort; - break; - case DCB_CONNECTOR_eDP: - type = DRM_MODE_CONNECTOR_eDP; - break; - default: - NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type); - return ERR_PTR(-EINVAL); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + nv_connector = nouveau_connector(connector); + if (nv_connector->index == index) + return connector; } nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); if (!nv_connector) return ERR_PTR(-ENOMEM); - nv_connector->dcb = dcb; + connector = &nv_connector->base; + nv_connector->index = index; + + /* attempt to parse vbios connector type and hotplug gpio */ + nv_connector->dcb = dcb_conn(dev, index); + if (nv_connector->dcb) { + static const u8 hpd[16] = { + 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60, + }; + + u32 entry = ROM16(nv_connector->dcb[0]); + if (dcb_conntab(dev)[3] >= 4) + entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; + + nv_connector->hpd = ffs((entry & 0x07033000) >> 12); + nv_connector->hpd = hpd[nv_connector->hpd]; + + nv_connector->type = nv_connector->dcb[0]; + if (drm_conntype_from_dcb(nv_connector->type) == + DRM_MODE_CONNECTOR_Unknown) { + NV_WARN(dev, "unknown connector type %02x\n", + nv_connector->type); + nv_connector->type = DCB_CONNECTOR_NONE; + } - /* defaults, will get overridden in detect() */ - connector->interlace_allowed = false; - connector->doublescan_allowed = false; + /* Gigabyte NX85T */ + if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } - drm_connector_init(dev, connector, funcs, type); - drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); + /* Gigabyte GV-NX86T512H */ + if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } + } else { + nv_connector->type = DCB_CONNECTOR_NONE; + nv_connector->hpd = DCB_GPIO_UNUSED; + } + + /* no vbios data, or an unknown dcb connector type - attempt to + * figure out something suitable ourselves + */ + if (nv_connector->type == DCB_CONNECTOR_NONE) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct dcb_table *dcbt = &dev_priv->vbios.dcb; + u32 encoders = 0; + int i; + + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector == nv_connector->index) + encoders |= (1 << dcbt->entry[i].type); + } - /* parse lvds table now, we depend on bios->fp.* values later */ - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { - bool dummy, is_24bit = false; + if (encoders & (1 << OUTPUT_DP)) { + if (encoders & (1 << OUTPUT_TMDS)) + nv_connector->type = DCB_CONNECTOR_DP; + else + nv_connector->type = DCB_CONNECTOR_eDP; + } else + if (encoders & (1 << OUTPUT_TMDS)) { + if (encoders & (1 << OUTPUT_ANALOG)) + nv_connector->type = DCB_CONNECTOR_DVI_I; + else + nv_connector->type = DCB_CONNECTOR_DVI_D; + } else + if (encoders & (1 << OUTPUT_ANALOG)) { + nv_connector->type = DCB_CONNECTOR_VGA; + } else + if (encoders & (1 << OUTPUT_LVDS)) { + nv_connector->type = DCB_CONNECTOR_LVDS; + } else + if (encoders & (1 << OUTPUT_TV)) { + nv_connector->type = DCB_CONNECTOR_TV_0; + } + } - ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit); + type = drm_conntype_from_dcb(nv_connector->type); + if (type == DRM_MODE_CONNECTOR_LVDS) { + ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); if (ret) { - NV_ERROR(dev, "Error parsing LVDS table, disabling " - "LVDS\n"); - goto fail; + NV_ERROR(dev, "Error parsing LVDS table, disabling\n"); + kfree(nv_connector); + return ERR_PTR(ret); } + + funcs = &nouveau_connector_funcs_lvds; + } else { + funcs = &nouveau_connector_funcs; } + /* defaults, will get overridden in detect() */ + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + drm_connector_init(dev, connector, funcs, type); + drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); + /* Init DVI-I specific properties */ - if (dcb->type == DCB_CONNECTOR_DVI_I) + if (nv_connector->type == DCB_CONNECTOR_DVI_I) drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0); /* Add overscan compensation options to digital outputs */ if (disp->underscan_property && - (dcb->type == DCB_CONNECTOR_DVI_D || - dcb->type == DCB_CONNECTOR_DVI_I || - dcb->type == DCB_CONNECTOR_HDMI_0 || - dcb->type == DCB_CONNECTOR_HDMI_1 || - dcb->type == DCB_CONNECTOR_DP)) { + (nv_connector->type == DCB_CONNECTOR_DVI_D || + nv_connector->type == DCB_CONNECTOR_DVI_I || + nv_connector->type == DCB_CONNECTOR_HDMI_0 || + nv_connector->type == DCB_CONNECTOR_HDMI_1 || + nv_connector->type == DCB_CONNECTOR_DP)) { drm_connector_attach_property(connector, disp->underscan_property, UNDERSCAN_OFF); @@ -960,7 +1026,7 @@ nouveau_connector_create(struct drm_device *dev, int index) 0); } - switch (dcb->type) { + switch (nv_connector->type) { case DCB_CONNECTOR_VGA: if (dev_priv->card_type >= NV_50) { drm_connector_attach_property(connector, @@ -994,8 +1060,8 @@ nouveau_connector_create(struct drm_device *dev, int index) break; } - if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) { - pgpio->irq_register(dev, nv_connector->dcb->gpio_tag, + if (nv_connector->hpd != DCB_GPIO_UNUSED && pgpio->irq_register) { + pgpio->irq_register(dev, nv_connector->hpd, nouveau_connector_hotplug, connector); connector->polled = DRM_CONNECTOR_POLL_HPD; @@ -1004,15 +1070,7 @@ nouveau_connector_create(struct drm_device *dev, int index) } drm_sysfs_connector_add(connector); - - dcb->drm = connector; - return dcb->drm; - -fail: - drm_connector_cleanup(connector); - kfree(connector); - return ERR_PTR(ret); - + return connector; } static void diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 6468f5e67ccf..e4857021304c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -56,8 +56,10 @@ enum nouveau_dithering_depth { struct nouveau_connector { struct drm_connector base; - - struct dcb_connector_table_entry *dcb; + enum dcb_connector_type type; + u8 index; + u8 *dcb; + u8 hpd; int dithering_mode; int dithering_depth; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 07bac3602453..02b00c827da3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -587,7 +587,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) * 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); + pgpio->irq_enable(dev, nv_connector->hpd, false); /* enable down-spreading, if possible */ if (dp.table[1] >= 16) { @@ -636,7 +636,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) 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); + pgpio->irq_enable(dev, nv_connector->hpd, true); return true; } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index a3d28a1e8bb2..9708b94a0a7b 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -241,10 +241,10 @@ nv50_display_init(struct drm_device *dev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (conn->dcb->gpio_tag == 0xff) + if (conn->hpd == DCB_GPIO_UNUSED) continue; - pgpio->irq_enable(dev, conn->dcb->gpio_tag, true); + pgpio->irq_enable(dev, conn->hpd, true); } ret = nv50_evo_init(dev); @@ -581,7 +581,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb, } else { /* determine number of lvds links */ if (nv_connector && nv_connector->edid && - nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { /* http://www.spwg.org */ if (((u8 *)nv_connector->edid)[121] == 2) script |= 0x0100; diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 89a31851e83a..d2ba2f07400b 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -1285,7 +1285,7 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, if (bios->fp.if_is_24bit) or_config |= 0x0200; } else { - if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { + if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { if (((u8 *)nv_connector->edid)[121] == 2) or_config |= 0x0100; } else -- cgit v1.2.3 From b4c26818aeb4159dd537eff6453ae5ebf7a69723 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 12 Oct 2011 16:36:42 +1000 Subject: drm/nouveau/mxm: initial implementation of dcb sanitisation The DCB table provided by the VBIOS on most MXM chips has a number of entries which either need to be disabled, or modified according to the MXM-SIS Output Device Descriptors. The x86 vbios code usually takes care of this for us, however, with the large number of laptops now with switchable graphics or optimus, a lot of the time nouveau is responsible for POSTing the card instead - leaving some fun situations like, plugging in a monitor and having nouveau decide 3 connectors actually just got plugged in.. No MXM-SIS fetching methods implemented yet. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 +- drivers/gpu/drm/nouveau/nouveau_bios.c | 10 +- drivers/gpu/drm/nouveau/nouveau_bios.h | 2 +- drivers/gpu/drm/nouveau/nouveau_drv.c | 4 + drivers/gpu/drm/nouveau/nouveau_drv.h | 6 + drivers/gpu/drm/nouveau/nouveau_mxm.c | 540 +++++++++++++++++++++++++++++++++ 6 files changed, 561 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nouveau_mxm.c (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index cdd9c3d0914d..c152a03d287f 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -11,7 +11,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ nouveau_hdmi.o nouveau_dp.o nouveau_ramht.o \ nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \ - nouveau_mm.o nouveau_vm.o \ + nouveau_mm.o nouveau_vm.o nouveau_mxm.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 35b18f4f7c4e..1faa7d9ceb94 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -5382,6 +5382,9 @@ bit_table(struct drm_device *dev, u8 id, struct bit_entry *bit) struct nvbios *bios = &dev_priv->vbios; u8 entries, *entry; + if (bios->type != NVBIOS_BIT) + return -ENODEV; + entries = bios->data[bios->offset + 10]; entry = &bios->data[bios->offset + 12]; while (entries--) { @@ -5832,7 +5835,7 @@ dcb_table(struct drm_device *dev) return NULL; } -u8 * +void * dcb_outp(struct drm_device *dev, u8 idx) { u8 *dcb = dcb_table(dev); @@ -6663,6 +6666,10 @@ nouveau_bios_init(struct drm_device *dev) if (ret) return ret; + ret = nouveau_mxm_init(dev); + if (ret) + return ret; + ret = parse_dcb_table(dev, bios); if (ret) return ret; @@ -6703,5 +6710,6 @@ nouveau_bios_init(struct drm_device *dev) void nouveau_bios_takedown(struct drm_device *dev) { + nouveau_mxm_fini(dev); nouveau_i2c_fini(dev); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 481e1f23f2f1..32b911d38e9a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -315,7 +315,7 @@ struct nvbios { }; void *dcb_table(struct drm_device *); -u8 *dcb_outp(struct drm_device *, u8 idx); +void *dcb_outp(struct drm_device *, u8 idx); int dcb_outp_foreach(struct drm_device *, void *data, int (*)(struct drm_device *, void *, int idx, u8 *outp)); u8 *dcb_conntab(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index e4485404892e..e4a7cfe7898d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -124,6 +124,10 @@ MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n"); int nouveau_ctxfw; module_param_named(ctxfw, nouveau_ctxfw, int, 0400); +MODULE_PARM_DESC(ctxfw, "Santise DCB table according to MXM-SIS\n"); +int nouveau_mxmdcb = 1; +module_param_named(mxmdcb, nouveau_mxmdcb, int, 0400); + int nouveau_fbpercrtc; #if 0 module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index fb126c1508b5..891c91b14886 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -793,6 +793,7 @@ struct drm_nouveau_private { struct nouveau_vm *chan_vm; struct nvbios vbios; + u8 *mxms; struct list_head i2c_ports; struct nv04_mode_state mode_reg; @@ -864,6 +865,7 @@ extern char *nouveau_perflvl; extern int nouveau_perflvl_wr; extern int nouveau_msi; extern int nouveau_ctxfw; +extern int nouveau_mxmdcb; extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); extern int nouveau_pci_resume(struct pci_dev *pdev); @@ -1108,6 +1110,10 @@ 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_mxm.c */ +int nouveau_mxm_init(struct drm_device *dev); +void nouveau_mxm_fini(struct drm_device *dev); + /* nouveau_ttm.c */ int nouveau_ttm_global_init(struct drm_nouveau_private *); void nouveau_ttm_global_release(struct drm_nouveau_private *); diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c new file mode 100644 index 000000000000..ae9a5ac750fd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -0,0 +1,540 @@ +/* + * 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" + +#define MXM_DBG(dev, fmt, args...) NV_DEBUG((dev), "MXM: " fmt, ##args) +#define MXM_MSG(dev, fmt, args...) NV_INFO((dev), "MXM: " fmt, ##args) + +static u8 * +mxms_data(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + return dev_priv->mxms; + +} + +static u16 +mxms_version(struct drm_device *dev) +{ + u8 *mxms = mxms_data(dev); + u16 version = (mxms[4] << 8) | mxms[5]; + switch (version ) { + case 0x0200: + case 0x0201: + case 0x0300: + return version; + default: + break; + } + + MXM_DBG(dev, "unknown version %d.%d\n", mxms[4], mxms[5]); + return 0x0000; +} + +static u16 +mxms_headerlen(struct drm_device *dev) +{ + return 8; +} + +static u16 +mxms_structlen(struct drm_device *dev) +{ + return *(u16 *)&mxms_data(dev)[6]; +} + +static bool +mxms_checksum(struct drm_device *dev) +{ + u16 size = mxms_headerlen(dev) + mxms_structlen(dev); + u8 *mxms = mxms_data(dev), sum = 0; + while (size--) + sum += *mxms++; + if (sum) { + MXM_DBG(dev, "checksum invalid\n"); + return false; + } + return true; +} + +static bool +mxms_valid(struct drm_device *dev) +{ + u8 *mxms = mxms_data(dev); + if (*(u32 *)mxms != 0x5f4d584d) { + MXM_DBG(dev, "signature invalid\n"); + return false; + } + + if (!mxms_version(dev) || !mxms_checksum(dev)) + return false; + + return true; +} + +static bool +mxms_foreach(struct drm_device *dev, u8 types, + bool (*exec)(struct drm_device *, u8 *, void *), void *info) +{ + u8 *mxms = mxms_data(dev); + u8 *desc = mxms + mxms_headerlen(dev); + u8 *fini = desc + mxms_structlen(dev) - 1; + while (desc < fini) { + u8 type = desc[0] & 0x0f; + u8 headerlen = 0; + u8 recordlen = 0; + u8 entries = 0; + + switch (type) { + case 0: /* Output Device Structure */ + if (mxms_version(dev) >= 0x0300) + headerlen = 8; + else + headerlen = 6; + break; + case 1: /* System Cooling Capability Structure */ + case 2: /* Thermal Structure */ + case 3: /* Input Power Structure */ + headerlen = 4; + break; + case 4: /* GPIO Device Structure */ + headerlen = 4; + recordlen = 2; + entries = (ROM32(desc[0]) & 0x01f00000) >> 20; + break; + case 5: /* Vendor Specific Structure */ + headerlen = 8; + break; + case 6: /* Backlight Control Structure */ + if (mxms_version(dev) >= 0x0300) { + headerlen = 4; + recordlen = 8; + entries = (desc[1] & 0xf0) >> 4; + } else { + headerlen = 8; + } + break; + case 7: /* Fan Control Structure */ + headerlen = 8; + recordlen = 4; + entries = desc[1] & 0x07; + break; + default: + MXM_DBG(dev, "unknown descriptor type %d\n", type); + return false; + } + + if ((drm_debug & DRM_UT_DRIVER) && (exec == NULL)) { + static const char * mxms_desc_name[] = { + "ODS", "SCCS", "TS", "IPS", + "GSD", "VSS", "BCS", "FCS", + }; + u8 *dump = desc; + int i, j; + + MXM_DBG(dev, "%4s: ", mxms_desc_name[type]); + for (j = headerlen - 1; j >= 0; j--) + printk("%02x", dump[j]); + printk("\n"); + dump += headerlen; + + for (i = 0; i < entries; i++, dump += recordlen) { + MXM_DBG(dev, " "); + for (j = recordlen - 1; j >= 0; j--) + printk("%02x", dump[j]); + printk("\n"); + } + } + + if (types & (1 << type)) { + if (!exec(dev, desc, info)) + return false; + } + + desc += headerlen + (entries * recordlen); + } + + return true; +} + +static u8 * +mxm_table(struct drm_device *dev, u8 *size) +{ + struct bit_entry x; + + if (bit_table(dev, 'x', &x)) { + MXM_DBG(dev, "BIT 'x' table not present\n"); + return NULL; + } + + if (x.version != 1 || x.length < 3) { + MXM_MSG(dev, "BIT x table %d/%d unknown\n", + x.version, x.length); + return NULL; + } + + *size = x.length; + return x.data; +} + +/* These map MXM v2.x digital connection values to the appropriate SOR/link, + * hopefully they're correct for all boards within the same chipset... + * + * MXM v3.x VBIOS are nicer and provide pointers to these tables. + */ +static u8 nv84_sor_map[16] = { + 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv92_sor_map[16] = { + 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv94_sor_map[16] = { + 0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv96_sor_map[16] = { + 0x00, 0x14, 0x24, 0x00, 0x34, 0x00, 0x11, 0x31, + 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 nv98_sor_map[16] = { + 0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 +mxm_sor_map(struct drm_device *dev, u8 conn) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u8 len, *mxm = mxm_table(dev, &len); + if (mxm && len >= 6) { + u8 *map = ROMPTR(dev, mxm[4]); + if (map) { + if (map[0] == 0x10) { + if (conn < map[3]) + return map[map[1] + conn]; + return 0x00; + } + + MXM_MSG(dev, "unknown sor map 0x%02x\n", map[0]); + } + } + + if (dev_priv->chipset == 0x84 || dev_priv->chipset == 0x86) + return nv84_sor_map[conn]; + if (dev_priv->chipset == 0x92) + return nv92_sor_map[conn]; + if (dev_priv->chipset == 0x94) + return nv94_sor_map[conn]; + if (dev_priv->chipset == 0x96) + return nv96_sor_map[conn]; + if (dev_priv->chipset == 0x98) + return nv98_sor_map[conn]; + + MXM_MSG(dev, "missing sor map\n"); + return 0x00; +} + +static u8 +mxm_ddc_map(struct drm_device *dev, u8 port) +{ + u8 len, *mxm = mxm_table(dev, &len); + if (mxm && len >= 8) { + u8 *map = ROMPTR(dev, mxm[6]); + if (map) { + if (map[0] == 0x10) { + if (port < map[3]) + return map[map[1] + port]; + return 0x00; + } + + MXM_MSG(dev, "unknown ddc map 0x%02x\n", map[0]); + } + } + + /* v2.x: directly write port as dcb i2cidx */ + return (port << 4) | port; +} + +struct mxms_odev { + u8 outp_type; + u8 conn_type; + u8 ddc_port; + u8 dig_conn; +}; + +static void +mxms_output_device(struct drm_device *dev, u8 *pdata, struct mxms_odev *desc) +{ + u64 data = ROM32(pdata[0]); + if (mxms_version(dev) >= 0x0300) + data |= (u64)ROM16(pdata[4]) << 32; + + desc->outp_type = (data & 0x00000000000000f0ULL) >> 4; + desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8; + desc->conn_type = (data & 0x000000000001f000ULL) >> 12; + desc->dig_conn = (data & 0x0000000000780000ULL) >> 19; +} + +struct context { + u32 *outp; + struct mxms_odev desc; +}; + +static bool +mxm_match_tmds_partner(struct drm_device *dev, u8 *data, void *info) +{ + struct context *ctx = info; + struct mxms_odev desc; + + mxms_output_device(dev, data, &desc); + if (desc.outp_type == 2 && + desc.dig_conn == ctx->desc.dig_conn) + return false; + return true; +} + +static bool +mxm_match_dcb(struct drm_device *dev, u8 *data, void *info) +{ + struct context *ctx = info; + u64 desc = *(u64 *)data; + + mxms_output_device(dev, data, &ctx->desc); + + /* match dcb encoder type to mxm-ods device type */ + if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type) + return true; + + /* digital output, have some extra stuff to match here, there's a + * table in the vbios that provides a mapping from the mxm digital + * connection enum values to SOR/link + */ + if ((desc & 0x00000000000000f0) >= 0x20) { + /* check against sor index */ + u8 link = mxm_sor_map(dev, ctx->desc.dig_conn); + if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24) + return true; + + /* check dcb entry has a compatible link field */ + link = (link & 0x30) >> 4; + if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link) + return true; + } + + /* mark this descriptor accounted for by setting invalid device type, + * except of course some manufactures don't follow specs properly and + * we need to avoid killing off the TMDS function on DP connectors + * if MXM-SIS is missing an entry for it. + */ + data[0] &= ~0xf0; + if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 && + mxms_foreach(dev, 0x01, mxm_match_tmds_partner, ctx)) { + data[0] |= 0x20; /* modify descriptor to match TMDS now */ + } else { + data[0] |= 0xf0; + } + + return false; +} + +static int +mxm_dcb_sanitise_entry(struct drm_device *dev, void *data, int idx, u8 *dcbe) +{ + struct context ctx = { .outp = (u32 *)dcbe }; + u8 type, i2cidx, link; + u8 *conn; + + /* look for an output device structure that matches this dcb entry. + * if one isn't found, disable it. + */ + if (mxms_foreach(dev, 0x01, mxm_match_dcb, &ctx)) { + MXM_DBG(dev, "disable %d: 0x%08x 0x%08x\n", + idx, ctx.outp[0], ctx.outp[1]); + ctx.outp[0] |= 0x0000000f; + return 0; + } + + /* modify the output's ddc/aux port, there's a pointer to a table + * with the mapping from mxm ddc/aux port to dcb i2c_index in the + * vbios mxm table + */ + i2cidx = mxm_ddc_map(dev, ctx.desc.ddc_port); + if ((ctx.outp[0] & 0x0000000f) != OUTPUT_DP) + i2cidx = (i2cidx & 0x0f) << 4; + else + i2cidx = (i2cidx & 0xf0); + + if (i2cidx != 0xf0) { + ctx.outp[0] &= ~0x000000f0; + ctx.outp[0] |= i2cidx; + } + + /* override dcb sorconf.link, based on what mxm data says */ + switch (ctx.desc.outp_type) { + case 0x00: /* Analog CRT */ + case 0x01: /* Analog TV/HDTV */ + break; + default: + link = mxm_sor_map(dev, ctx.desc.dig_conn) & 0x30; + ctx.outp[1] &= ~0x00000030; + ctx.outp[1] |= link; + break; + } + + /* we may need to fixup various other vbios tables based on what + * the descriptor says the connector type should be. + * + * in a lot of cases, the vbios tables will claim DVI-I is possible, + * and the mxm data says the connector is really HDMI. another + * common example is DP->eDP. + */ + conn = dcb_conn(dev, (ctx.outp[0] & 0x0000f000) >> 12); + type = conn[0]; + switch (ctx.desc.conn_type) { + case 0x01: /* LVDS */ + ctx.outp[1] |= 0x00000004; /* use_power_scripts */ + /* XXX: modify default link width in LVDS table */ + break; + case 0x02: /* HDMI */ + type = DCB_CONNECTOR_HDMI_1; + break; + case 0x03: /* DVI-D */ + type = DCB_CONNECTOR_DVI_D; + break; + case 0x0e: /* eDP, falls through to DPint */ + ctx.outp[1] |= 0x00010000; + case 0x07: /* DP internal, wtf is this?? HP8670w */ + ctx.outp[1] |= 0x00000004; /* use_power_scripts? */ + type = DCB_CONNECTOR_eDP; + break; + default: + break; + } + + if (mxms_version(dev) >= 0x0300) + conn[0] = type; + + return 0; +} + +static bool +mxm_show_unmatched(struct drm_device *dev, u8 *data, void *info) +{ + u64 desc = *(u64 *)data; + if ((desc & 0xf0) != 0xf0) + MXM_MSG(dev, "unmatched output device 0x%016llx\n", desc); + return true; +} + +static void +mxm_dcb_sanitise(struct drm_device *dev) +{ + u8 *dcb = dcb_table(dev); + if (!dcb || dcb[0] != 0x40) { + MXM_DBG(dev, "unsupported DCB version\n"); + return; + } + + dcb_outp_foreach(dev, NULL, mxm_dcb_sanitise_entry); + mxms_foreach(dev, 0x01, mxm_show_unmatched, NULL); +} + +static bool +mxm_shadow_rom(struct drm_device *dev) +{ + return false; +} + +static bool +mxm_shadow_dsm(struct drm_device *dev) +{ + return false; +} + +struct mxm_shadow_h { + const char *name; + bool (*exec)(struct drm_device *); +} _mxm_shadow[] = { + { "ROM", mxm_shadow_rom }, + { "DSM", mxm_shadow_dsm }, + {} +}; + +static int +mxm_shadow(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct mxm_shadow_h *shadow = _mxm_shadow; + do { + MXM_DBG(dev, "checking %s\n", shadow->name); + if (shadow->exec(dev)) { + if (mxms_valid(dev)) + return 0; + kfree(dev_priv->mxms); + dev_priv->mxms = NULL; + } + } while ((++shadow)->name); + return -ENOENT; +} + +int +nouveau_mxm_init(struct drm_device *dev) +{ + u8 mxm_size, *mxm = mxm_table(dev, &mxm_size); + if (!mxm || !mxm[0]) { + MXM_MSG(dev, "no VBIOS data, nothing to do\n"); + return 0; + } + + MXM_MSG(dev, "BIOS version %d.%d\n", mxm[0] >> 4, mxm[0] & 0x0f); + + if (mxm_shadow(dev)) { + MXM_MSG(dev, "failed to locate valid SIS\n"); + return -EINVAL; + } + + MXM_MSG(dev, "MXMS Version %d.%d\n", + mxms_version(dev) >> 8, mxms_version(dev) & 0xff); + mxms_foreach(dev, 0, NULL, NULL); + + if (nouveau_mxmdcb) + mxm_dcb_sanitise(dev); + return 0; +} + +void +nouveau_mxm_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + kfree(dev_priv->mxms); + dev_priv->mxms = NULL; +} -- cgit v1.2.3 From 93d9206d08dd18e3aaeed90a3e076b8c323fdd72 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 7 Nov 2011 12:54:20 +1000 Subject: drm/nouveau/mxm: implement wmi shadow method Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_mxm.c | 51 ++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c index ae9a5ac750fd..f6f73568c4f0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs */ +#include + #include "drmP.h" #include "nouveau_drv.h" @@ -469,34 +471,71 @@ mxm_dcb_sanitise(struct drm_device *dev) } static bool -mxm_shadow_rom(struct drm_device *dev) +mxm_shadow_rom(struct drm_device *dev, u8 version) { return false; } static bool -mxm_shadow_dsm(struct drm_device *dev) +mxm_shadow_dsm(struct drm_device *dev, u8 version) { return false; } +#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) + +#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" + +static bool +mxm_shadow_wmi(struct drm_device *dev, u8 version) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 }; + struct acpi_buffer args = { sizeof(mxms_args), mxms_args }; + struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + if (!wmi_has_guid(WMI_WMMX_GUID)) + return false; + + status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); + if (ACPI_FAILURE(status)) { + MXM_DBG(dev, "WMMX MXMS returned %d\n", status); + return false; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_BUFFER) { + dev_priv->mxms = kmemdup(obj->buffer.pointer, + obj->buffer.length, GFP_KERNEL); + } + + kfree(obj); + return dev_priv->mxms != NULL; +} +#endif + struct mxm_shadow_h { const char *name; - bool (*exec)(struct drm_device *); + bool (*exec)(struct drm_device *, u8 version); } _mxm_shadow[] = { { "ROM", mxm_shadow_rom }, { "DSM", mxm_shadow_dsm }, +#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) + { "WMI", mxm_shadow_wmi }, +#endif {} }; static int -mxm_shadow(struct drm_device *dev) +mxm_shadow(struct drm_device *dev, u8 version) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct mxm_shadow_h *shadow = _mxm_shadow; do { MXM_DBG(dev, "checking %s\n", shadow->name); - if (shadow->exec(dev)) { + if (shadow->exec(dev, version)) { if (mxms_valid(dev)) return 0; kfree(dev_priv->mxms); @@ -517,7 +556,7 @@ nouveau_mxm_init(struct drm_device *dev) MXM_MSG(dev, "BIOS version %d.%d\n", mxm[0] >> 4, mxm[0] & 0x0f); - if (mxm_shadow(dev)) { + if (mxm_shadow(dev, mxm[0])) { MXM_MSG(dev, "failed to locate valid SIS\n"); return -EINVAL; } -- cgit v1.2.3 From 3952315b9d20fb04d43d184f9c1475327811c5aa Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 7 Nov 2011 13:36:08 +1000 Subject: drm/nouveau/mxm: implement _DSM shadow method Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_mxm.c | 61 ++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c index f6f73568c4f0..840c88fc4f0e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -476,11 +476,68 @@ mxm_shadow_rom(struct drm_device *dev, u8 version) return false; } +#if defined(CONFIG_ACPI) static bool mxm_shadow_dsm(struct drm_device *dev, u8 version) { - return false; + struct drm_nouveau_private *dev_priv = dev->dev_private; + static char muid[] = { + 0x00, 0xA4, 0x04, 0x40, 0x7D, 0x91, 0xF2, 0x4C, + 0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65 + }; + u32 mxms_args[] = { 0x00000000 }; + union acpi_object args[4] = { + /* _DSM MUID */ + { .buffer.type = 3, + .buffer.length = sizeof(muid), + .buffer.pointer = muid, + }, + /* spec says this can be zero to mean "highest revision", but + * of course there's at least one bios out there which fails + * unless you pass in exactly the version it supports.. + */ + { .integer.type = ACPI_TYPE_INTEGER, + .integer.value = (version & 0xf0) << 4 | (version & 0x0f), + }, + /* MXMS function */ + { .integer.type = ACPI_TYPE_INTEGER, + .integer.value = 0x00000010, + }, + /* Pointer to MXMS arguments */ + { .buffer.type = ACPI_TYPE_BUFFER, + .buffer.length = sizeof(mxms_args), + .buffer.pointer = (char *)mxms_args, + }, + }; + struct acpi_object_list list = { ARRAY_SIZE(args), args }; + struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_handle handle; + int ret; + + handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); + if (!handle) + return false; + + ret = acpi_evaluate_object(handle, "_DSM", &list, &retn); + if (ret) { + MXM_DBG(dev, "DSM MXMS failed: %d\n", ret); + return false; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_BUFFER) { + dev_priv->mxms = kmemdup(obj->buffer.pointer, + obj->buffer.length, GFP_KERNEL); + } else + if (obj->type == ACPI_TYPE_INTEGER) { + MXM_DBG(dev, "DSM MXMS returned 0x%llx\n", obj->integer.value); + } + + kfree(obj); + return dev_priv->mxms != NULL; } +#endif #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) @@ -521,7 +578,9 @@ struct mxm_shadow_h { bool (*exec)(struct drm_device *, u8 version); } _mxm_shadow[] = { { "ROM", mxm_shadow_rom }, +#if defined(CONFIG_ACPI) { "DSM", mxm_shadow_dsm }, +#endif #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) { "WMI", mxm_shadow_wmi }, #endif -- cgit v1.2.3 From c37e99050c84c40441e614bd41474e12b6cc2079 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 7 Nov 2011 16:16:24 +1000 Subject: drm/nouveau/mxm: implement ROM shadow method Untested, -ENOHW. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_mxm.c | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c index 840c88fc4f0e..8bccddf4eff0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -470,9 +470,48 @@ mxm_dcb_sanitise(struct drm_device *dev) mxms_foreach(dev, 0x01, mxm_show_unmatched, NULL); } +static bool +mxm_shadow_rom_fetch(struct nouveau_i2c_chan *i2c, u8 addr, + u8 offset, u8 size, u8 *data) +{ + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 1, .buf = &offset }, + { .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, }, + }; + + return i2c_transfer(&i2c->adapter, msgs, 2) == 2; +} + static bool mxm_shadow_rom(struct drm_device *dev, u8 version) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_i2c_chan *i2c = NULL; + u8 i2cidx, mxms[6], addr, size; + + i2cidx = mxm_ddc_map(dev, 1 /* LVDS_DDC */) & 0x0f; + if (i2cidx < 0x0f) + i2c = nouveau_i2c_find(dev, i2cidx); + if (!i2c) + return false; + + addr = 0x54; + if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms)) { + addr = 0x56; + if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms)) + return false; + } + + dev_priv->mxms = mxms; + size = mxms_headerlen(dev) + mxms_structlen(dev); + dev_priv->mxms = kmalloc(size, GFP_KERNEL); + + if (dev_priv->mxms && + mxm_shadow_rom_fetch(i2c, addr, 0, size, dev_priv->mxms)) + return true; + + kfree(dev_priv->mxms); + dev_priv->mxms = NULL; return false; } -- cgit v1.2.3 From d4c2c99bdc8385a0e51ce4ef2df124d14b6b9c9d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 15 Nov 2011 14:31:13 +1000 Subject: drm/nouveau/dp: remove broken display depth function, use the improved one Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 34 ++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f6c8e1da4228..df99c7f1191a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -78,23 +78,6 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder) return NULL; } -/*TODO: This could use improvement, and learn to handle the fixed - * BIOS tables etc. It's fine currently, for its only user. - */ -int -nouveau_connector_bpp(struct drm_connector *connector) -{ - struct nouveau_connector *nv_connector = nouveau_connector(connector); - - if (nv_connector->edid && nv_connector->edid->revision >= 4) { - u8 bpc = ((nv_connector->edid->input & 0x70) >> 3) + 4; - if (bpc > 4) - return bpc; - } - - return 18; -} - static void nouveau_connector_destroy(struct drm_connector *connector) { @@ -714,6 +697,12 @@ nouveau_connector_get_modes(struct drm_connector *connector) nv_connector->native_mode = drm_mode_duplicate(dev, &mode); } + /* Determine display colour depth for everything except LVDS now, + * DP requires this before mode_valid() is called. + */ + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); + /* Find the native mode if this is a digital panel, if we didn't * find any modes through DDC previously add the native mode to * the list of modes. @@ -729,11 +718,12 @@ nouveau_connector_get_modes(struct drm_connector *connector) ret = 1; } - /* Attempt to determine display colour depth, this has to happen after - * we've determined the "native" mode for LVDS, as the VBIOS tables - * require us to compare against a pixel clock in some cases.. + /* Determine LVDS colour depth, must happen after determining + * "native" mode as some VBIOS tables require us to use the + * pixel clock as part of the lookup... */ - nouveau_connector_detect_depth(connector); + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); if (nv_encoder->dcb->type == OUTPUT_TV) ret = get_slave_funcs(encoder)->get_modes(encoder, connector); @@ -799,7 +789,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, case OUTPUT_DP: max_clock = nv_encoder->dp.link_nr; max_clock *= nv_encoder->dp.link_bw; - clock = clock * nouveau_connector_bpp(connector) / 10; + clock = clock * (connector->display_info.bpc * 3) / 10; break; default: BUG_ON(1); -- cgit v1.2.3 From abbd3f8e3bea4b2b0490260e67357067a2dc2039 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 18 Nov 2011 11:55:43 +1000 Subject: drm/nv04/disp: handle dual-link spwg panels without needing quirks Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 12 ------------ drivers/gpu/drm/nouveau/nv04_dfp.c | 11 ++++++++--- 2 files changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 1faa7d9ceb94..f8df37285ba7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -4304,18 +4304,6 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b break; } - /* Dell Latitude D620 reports a too-high value for the dual-link - * transition freq, causing us to program the panel incorrectly. - * - * It doesn't appear the VBIOS actually uses its transition freq - * (90000kHz), instead it uses the "Number of LVDS channels" field - * out of the panel ID structure (http://www.spwg.org/). - * - * For the moment, a quirk will do :) - */ - if (nv_match_device(dev, 0x01d7, 0x1028, 0x01c2)) - bios->fp.duallink_transition_clk = 80000; - /* set dual_link flag for EDID case */ if (pxclk && (chip_version < 0x25 || chip_version > 0x28)) bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk); diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index dff3ad594e0e..2258746016f8 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -341,10 +341,15 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, output_mode->clock > 165000) regp->fp_control |= (2 << 24); if (nv_encoder->dcb->type == OUTPUT_LVDS) { - bool duallink, dummy; + bool duallink = false, dummy; + if (nv_connector->edid && + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { + duallink = (((u8 *)nv_connector->edid)[121] == 2); + } else { + nouveau_bios_parse_lvds_table(dev, output_mode->clock, + &duallink, &dummy); + } - nouveau_bios_parse_lvds_table(dev, output_mode->clock, - &duallink, &dummy); if (duallink) regp->fp_control |= (8 << 28); } else -- cgit v1.2.3 From eeb7a50bddb281d7beecb0ad73c9f1233e9932c2 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Mon, 7 Nov 2011 23:38:50 +0100 Subject: drm/nv50/pm: introduce hwsq-based memory reclocking More work needs to be done on supporting the different memory types. v2 (Ben Skeggs): - fixed up conflicts from not having pausing patch first - restructured code somewhat to fit with how all the other code works - fixed bug where incorrect mpll_ctrl could get set sometimes - removed stuff that's cargo-culted from the binary driver - merged nv92+ display disable into hwsq - fixed incorrect opcode 0x5f magic at end of ucode Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_hwsq.h | 98 ++++++++++++++++ drivers/gpu/drm/nouveau/nv50_pm.c | 207 +++++++++++++++++++++++---------- 2 files changed, 243 insertions(+), 62 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nouveau_hwsq.h (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_hwsq.h b/drivers/gpu/drm/nouveau/nouveau_hwsq.h new file mode 100644 index 000000000000..d59a3b3ff644 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_hwsq.h @@ -0,0 +1,98 @@ +/* + * Copyright 2010 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 + */ + +#ifndef __NOUVEAU_HWSQ_H__ +#define __NOUVEAU_HWSQ_H__ + +struct hwsq_ucode { + u8 data[0x200]; + union { + u8 *u08; + u16 *u16; + u32 *u32; + } ptr; + u16 len; + + u32 reg; + u32 val; +}; + +static inline void +hwsq_init(struct hwsq_ucode *hwsq) +{ + hwsq->ptr.u08 = hwsq->data; + hwsq->reg = 0xffffffff; + hwsq->val = 0xffffffff; +} + +static inline void +hwsq_fini(struct hwsq_ucode *hwsq) +{ + do { + *hwsq->ptr.u08++ = 0x7f; + hwsq->len = hwsq->ptr.u08 - hwsq->data; + } while (hwsq->len & 3); + hwsq->ptr.u08 = hwsq->data; +} + +static inline void +hwsq_unkn(struct hwsq_ucode *hwsq, u8 v0) +{ + *hwsq->ptr.u08++ = v0; +} + +static inline void +hwsq_op5f(struct hwsq_ucode *hwsq, u8 v0, u8 v1) +{ + *hwsq->ptr.u08++ = 0x5f; + *hwsq->ptr.u08++ = v0; + *hwsq->ptr.u08++ = v1; +} + +static inline void +hwsq_wr32(struct hwsq_ucode *hwsq, u32 reg, u32 val) +{ + if (val != hwsq->val) { + if ((val & 0xffff0000) == (hwsq->val & 0xffff0000)) { + *hwsq->ptr.u08++ = 0x42; + *hwsq->ptr.u16++ = (val & 0x0000ffff); + } else { + *hwsq->ptr.u08++ = 0xe2; + *hwsq->ptr.u32++ = val; + } + + hwsq->val = val; + } + + if ((reg & 0xffff0000) == (hwsq->reg & 0xffff0000)) { + *hwsq->ptr.u08++ = 0x40; + *hwsq->ptr.u16++ = (reg & 0x0000ffff); + } else { + *hwsq->ptr.u08++ = 0xe0; + *hwsq->ptr.u32++ = reg; + } + hwsq->reg = reg; +} + +#endif diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index e025cae5ef23..22789db48969 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -27,6 +27,7 @@ #include "nouveau_bios.h" #include "nouveau_hw.h" #include "nouveau_pm.h" +#include "nouveau_hwsq.h" enum clk_src { clk_src_crystal, @@ -351,6 +352,9 @@ nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } struct nv50_pm_state { + struct hwsq_ucode mclk_hwsq; + u32 mscript; + u32 emast; u32 nctrl; u32 ncoef; @@ -359,10 +363,6 @@ struct nv50_pm_state { u32 amast; u32 pdivs; - - u32 mscript; - u32 mctrl; - u32 mcoef; }; static u32 @@ -415,6 +415,80 @@ clk_same(u32 a, u32 b) return ((a / 1000) == (b / 1000)); } +static int +calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct pll_lims pll; + u32 mast = nv_rd32(dev, 0x00c040); + u32 ctrl = nv_rd32(dev, 0x004008); + u32 coef = nv_rd32(dev, 0x00400c); + u32 orig = ctrl; + u32 crtc_mask = 0; + int N, M, P; + int ret, i; + + /* use pcie refclock if possible, otherwise use mpll */ + ctrl &= ~0x81ff0200; + if (clk_same(freq, read_clk(dev, clk_src_href))) { + ctrl |= 0x00000200 | (pll.log2p_bias << 19); + } else { + ret = calc_pll(dev, 0x4008, &pll, freq, &N, &M, &P); + if (ret == 0) + return -EINVAL; + + ctrl |= 0x80000000 | (P << 22) | (P << 16); + ctrl |= pll.log2p_bias << 19; + coef = (N << 8) | M; + } + + mast &= ~0xc0000000; /* get MCLK_2 from HREF */ + mast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */ + + /* determine active crtcs */ + for (i = 0; i < 2; i++) { + if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK))) + crtc_mask |= (1 << i); + } + + /* build the ucode which will reclock the memory for us */ + hwsq_init(hwsq); + if (crtc_mask) { + hwsq_op5f(hwsq, crtc_mask, 0x00); /* wait for scanout */ + hwsq_op5f(hwsq, crtc_mask, 0x01); /* wait for vblank */ + } + if (dev_priv->chipset >= 0x92) + hwsq_wr32(hwsq, 0x611200, 0x00003300); /* disable scanout */ + hwsq_unkn(hwsq, 0xb0); /* disable bus access */ + hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */ + + /* prepare memory controller */ + hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ + hwsq_wr32(hwsq, 0x1002d0, 0x00000001); /* force refresh */ + hwsq_wr32(hwsq, 0x100210, 0x00000000); /* stop the automatic refresh */ + hwsq_wr32(hwsq, 0x1002dc, 0x00000001); /* start self refresh mode */ + + /* reclock memory */ + hwsq_wr32(hwsq, 0xc040, mast); + hwsq_wr32(hwsq, 0x4008, orig | 0x00000200); /* bypass MPLL */ + hwsq_wr32(hwsq, 0x400c, coef); + hwsq_wr32(hwsq, 0x4008, ctrl); + + /* restart memory controller */ + hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ + hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */ + hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */ + hwsq_unkn(hwsq, 0x07); /* wait for the PLL to stabilize (12us) */ + + hwsq_unkn(hwsq, 0x0b); /* may be unnecessary: causes flickering */ + hwsq_unkn(hwsq, 0xd0); /* enable bus access again */ + hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */ + if (dev_priv->chipset >= 0x92) + hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */ + hwsq_fini(hwsq); + return 0; +} + void * nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { @@ -462,23 +536,16 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) info->scoef = (N << 8) | M; } - /* memory: use pcie refclock if possible, otherwise use mpll */ - info->mscript = perflvl->memscript; - if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) { - info->mctrl = 0x00000200 | (pll.log2p_bias << 19); - info->mcoef = nv_rd32(dev, 0x400c); - } else + /* memory: build hwsq ucode which we'll use to reclock memory */ + info->mclk_hwsq.len = 0; if (perflvl->memory) { - clk = calc_pll(dev, 0x4008, &pll, perflvl->memory, - &N, &M, &P1); - if (clk == 0) + clk = calc_mclk(dev, perflvl->memory, &info->mclk_hwsq); + if (clk < 0) { + ret = clk; goto error; + } - info->mctrl = 0x80000000 | (P1 << 22) | (P1 << 16); - info->mctrl |= pll.log2p_bias << 19; - info->mcoef = (N << 8) | M; - } else { - info->mctrl = 0x00000000; + info->mscript = perflvl->memscript; } /* vdec: avoid modifying xpll until we know exactly how the other @@ -537,6 +604,44 @@ error: return ERR_PTR(ret); } +static int +prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 hwsq_data, hwsq_kick; + int i; + + if (dev_priv->chipset < 0x90) { + hwsq_data = 0x001400; + hwsq_kick = 0x00000003; + } else { + hwsq_data = 0x080000; + hwsq_kick = 0x00000001; + } + + /* upload hwsq ucode */ + nv_mask(dev, 0x001098, 0x00000008, 0x00000000); + nv_wr32(dev, 0x001304, 0x00000000); + for (i = 0; i < hwsq->len / 4; i++) + nv_wr32(dev, hwsq_data + (i * 4), hwsq->ptr.u32[i]); + nv_mask(dev, 0x001098, 0x00000018, 0x00000018); + + /* launch, and wait for completion */ + nv_wr32(dev, 0x00130c, hwsq_kick); + if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) { + NV_ERROR(dev, "hwsq ucode exec timed out\n"); + NV_ERROR(dev, "0x001308: 0x%08x\n", nv_rd32(dev, 0x001308)); + for (i = 0; i < hwsq->len / 4; i++) { + NV_ERROR(dev, "0x%06x: 0x%08x\n", 0x1400 + (i * 4), + nv_rd32(dev, 0x001400 + (i * 4))); + } + + return -EIO; + } + + return 0; +} + int nv50_pm_clocks_set(struct drm_device *dev, void *data) { @@ -550,6 +655,28 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) goto error; + /* memory: it is *very* important we change this first, the ucode + * we build in pre() now has hardcoded 0xc040 values, which can't + * change before we execute it or the engine clocks may end up + * messed up. + */ + if (info->mclk_hwsq.len) { + /* execute some scripts that do ??? from the vbios.. */ + if (!bit_table(dev, 'M', &M) && M.version == 1) { + if (M.length >= 6) + nouveau_bios_init_exec(dev, ROM16(M.data[5])); + if (M.length >= 8) + nouveau_bios_init_exec(dev, ROM16(M.data[7])); + if (M.length >= 10) + nouveau_bios_init_exec(dev, ROM16(M.data[9])); + nouveau_bios_init_exec(dev, info->mscript); + } + + ret = prog_mclk(dev, &info->mclk_hwsq); + if (ret) + goto resume; + } + /* reclock vdec/dom6 */ nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000); switch (dev_priv->chipset) { @@ -578,50 +705,6 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) nv_wr32(dev, 0x00402c, info->ncoef); nv_mask(dev, 0x00c040, 0x00100033, info->emast); - /* memory */ - if (!info->mctrl) - goto resume; - - /* execute some scripts that do ??? from the vbios.. */ - if (!bit_table(dev, 'M', &M) && M.version == 1) { - if (M.length >= 6) - nouveau_bios_init_exec(dev, ROM16(M.data[5])); - if (M.length >= 8) - nouveau_bios_init_exec(dev, ROM16(M.data[7])); - if (M.length >= 10) - nouveau_bios_init_exec(dev, ROM16(M.data[9])); - nouveau_bios_init_exec(dev, info->mscript); - } - - /* disable display */ - if (dev_priv->chipset >= 0x92) { - nv_wr32(dev, 0x611200, 0x00003300); - udelay(100); - } - - /* 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 */ - - /* modify mpll */ - nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000); - nv_mask(dev, 0x004008, 0x01ff0200, 0x00000200 | info->mctrl); - nv_wr32(dev, 0x00400c, info->mcoef); - udelay(100); - nv_mask(dev, 0x004008, 0x81ff0200, info->mctrl); - - /* re-enable normal operation of memory controller */ - nv_wr32(dev, 0x1002dc, 0x00000000); - nv_mask(dev, 0x100210, 0x80000000, 0x80000000); - udelay(100); - - /* re-enable display */ - if (dev_priv->chipset >= 0x92) - nv_wr32(dev, 0x611200, 0x00003330); - goto resume; error: ret = -EBUSY; -- cgit v1.2.3 From c8b9641a9146b13d34824f99c3d22c8c0c3a06bd Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 9 Nov 2011 20:22:25 +1000 Subject: drm/nouveau/hwsq: remove some magic, give proper opcode names Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_hwsq.h | 21 +++++++++++++++++++-- drivers/gpu/drm/nouveau/nv50_pm.c | 8 ++++---- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_hwsq.h b/drivers/gpu/drm/nouveau/nouveau_hwsq.h index d59a3b3ff644..697687593a81 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hwsq.h +++ b/drivers/gpu/drm/nouveau/nouveau_hwsq.h @@ -57,9 +57,26 @@ hwsq_fini(struct hwsq_ucode *hwsq) } static inline void -hwsq_unkn(struct hwsq_ucode *hwsq, u8 v0) +hwsq_usec(struct hwsq_ucode *hwsq, u8 usec) { - *hwsq->ptr.u08++ = v0; + u32 shift = 0; + while (usec & ~3) { + usec >>= 2; + shift++; + } + + *hwsq->ptr.u08++ = (shift << 2) | usec; +} + +static inline void +hwsq_setf(struct hwsq_ucode *hwsq, u8 flag, int val) +{ + flag += 0x80; + if (val >= 0) + flag += 0x20; + if (val >= 1) + flag += 0x20; + *hwsq->ptr.u08++ = flag; } static inline void diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 22789db48969..fce1214c3db1 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -459,7 +459,7 @@ calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) } if (dev_priv->chipset >= 0x92) hwsq_wr32(hwsq, 0x611200, 0x00003300); /* disable scanout */ - hwsq_unkn(hwsq, 0xb0); /* disable bus access */ + hwsq_setf(hwsq, 0x10, 0); /* disable bus access */ hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */ /* prepare memory controller */ @@ -478,10 +478,10 @@ calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */ hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */ - hwsq_unkn(hwsq, 0x07); /* wait for the PLL to stabilize (12us) */ + hwsq_usec(hwsq, 12); /* wait for the PLL to stabilize */ - hwsq_unkn(hwsq, 0x0b); /* may be unnecessary: causes flickering */ - hwsq_unkn(hwsq, 0xd0); /* enable bus access again */ + hwsq_usec(hwsq, 48); /* may be unnecessary: causes flickering */ + hwsq_setf(hwsq, 0x10, 1); /* enable bus access */ hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */ if (dev_priv->chipset >= 0x92) hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */ -- cgit v1.2.3 From 675aac033e089833e763ea4fbabae66883d10574 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 21 Nov 2011 21:28:28 +1000 Subject: drm/nouveau: just pass gpio line to pwm_*, not entire gpio struct We don't need more than the line id to determine the PWM controller, and the GPIO interfaces are about to change somewhat. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 4 ++-- drivers/gpu/drm/nouveau/nouveau_pm.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_pm.h | 8 ++++---- drivers/gpu/drm/nouveau/nv40_pm.c | 18 ++++++++---------- drivers/gpu/drm/nouveau/nv50_pm.c | 21 +++++++++------------ 5 files changed, 25 insertions(+), 30 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 891c91b14886..909b991416ed 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -556,8 +556,8 @@ struct nouveau_pm_engine { int (*voltage_get)(struct drm_device *); int (*voltage_set)(struct drm_device *, int voltage); - int (*pwm_get)(struct drm_device *, struct dcb_gpio_entry*, u32*, u32*); - int (*pwm_set)(struct drm_device *, struct dcb_gpio_entry*, u32, u32); + int (*pwm_get)(struct drm_device *, int line, u32*, u32*); + int (*pwm_set)(struct drm_device *, int line, u32, u32); int (*temp_get)(struct drm_device *); }; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 1442d0673671..788ba33da77c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -49,7 +49,7 @@ nouveau_pwmfan_get(struct drm_device *dev) gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN); if (gpio) { - ret = pm->pwm_get(dev, gpio, &divs, &duty); + ret = pm->pwm_get(dev, gpio->line, &divs, &duty); if (ret == 0) { divs = max(divs, duty); if (dev_priv->card_type <= NV_40 || @@ -90,7 +90,7 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) (gpio->state[0] & 1)) duty = divs - duty; - return pm->pwm_set(dev, gpio, divs, duty); + return pm->pwm_set(dev, gpio->line, divs, duty); } return -ENODEV; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 663088d30428..7e0cc2eeb307 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -55,15 +55,15 @@ int nv04_pm_clocks_set(struct drm_device *, void *); int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); int nv40_pm_clocks_set(struct drm_device *, void *); -int nv40_pm_pwm_get(struct drm_device *, struct dcb_gpio_entry *, u32*, u32*); -int nv40_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); +int nv40_pm_pwm_get(struct drm_device *, int, u32 *, u32 *); +int nv40_pm_pwm_set(struct drm_device *, int, u32, u32); /* nv50_pm.c */ int nv50_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); void *nv50_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); int nv50_pm_clocks_set(struct drm_device *, void *); -int nv50_pm_pwm_get(struct drm_device *, struct dcb_gpio_entry *, u32*, u32*); -int nv50_pm_pwm_set(struct drm_device *, struct dcb_gpio_entry *, u32, u32); +int nv50_pm_pwm_get(struct drm_device *, int, u32 *, u32 *); +int nv50_pm_pwm_set(struct drm_device *, int, u32, u32); /* nva3_pm.c */ int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index 3d5a5a7856e6..c7615381c5d9 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -351,10 +351,9 @@ resume: } int -nv40_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, - u32 *divs, u32 *duty) +nv40_pm_pwm_get(struct drm_device *dev, int line, u32 *divs, u32 *duty) { - if (gpio->line == 2) { + if (line == 2) { u32 reg = nv_rd32(dev, 0x0010f0); if (reg & 0x80000000) { *duty = (reg & 0x7fff0000) >> 16; @@ -362,7 +361,7 @@ nv40_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, return 0; } } else - if (gpio->line == 9) { + if (line == 9) { u32 reg = nv_rd32(dev, 0x0015f4); if (reg & 0x80000000) { *divs = nv_rd32(dev, 0x0015f8); @@ -370,7 +369,7 @@ nv40_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, return 0; } } else { - NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", gpio->line); + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", line); return -ENODEV; } @@ -378,17 +377,16 @@ nv40_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, } int -nv40_pm_pwm_set(struct drm_device *dev, struct dcb_gpio_entry *gpio, - u32 divs, u32 duty) +nv40_pm_pwm_set(struct drm_device *dev, int line, u32 divs, u32 duty) { - if (gpio->line == 2) { + if (line == 2) { nv_wr32(dev, 0x0010f0, 0x80000000 | (duty << 16) | divs); } else - if (gpio->line == 9) { + if (line == 9) { nv_wr32(dev, 0x0015f8, divs); nv_wr32(dev, 0x0015f4, duty | 0x80000000); } else { - NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", gpio->line); + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", line); return -ENODEV; } diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index fce1214c3db1..03937212e9d8 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -715,25 +715,24 @@ resume: } static int -pwm_info(struct drm_device *dev, struct dcb_gpio_entry *gpio, - int *ctrl, int *line, int *indx) +pwm_info(struct drm_device *dev, int *line, int *ctrl, int *indx) { - if (gpio->line == 0x04) { + if (*line == 0x04) { *ctrl = 0x00e100; *line = 4; *indx = 0; } else - if (gpio->line == 0x09) { + if (*line == 0x09) { *ctrl = 0x00e100; *line = 9; *indx = 1; } else - if (gpio->line == 0x10) { + if (*line == 0x10) { *ctrl = 0x00e28c; *line = 0; *indx = 0; } else { - NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", gpio->line); + NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", *line); return -ENODEV; } @@ -741,10 +740,9 @@ pwm_info(struct drm_device *dev, struct dcb_gpio_entry *gpio, } int -nv50_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, - u32 *divs, u32 *duty) +nv50_pm_pwm_get(struct drm_device *dev, int line, u32 *divs, u32 *duty) { - int ctrl, line, id, ret = pwm_info(dev, gpio, &ctrl, &line, &id); + int ctrl, id, ret = pwm_info(dev, &line, &ctrl, &id); if (ret) return ret; @@ -758,10 +756,9 @@ nv50_pm_pwm_get(struct drm_device *dev, struct dcb_gpio_entry *gpio, } int -nv50_pm_pwm_set(struct drm_device *dev, struct dcb_gpio_entry *gpio, - u32 divs, u32 duty) +nv50_pm_pwm_set(struct drm_device *dev, int line, u32 divs, u32 duty) { - int ctrl, line, id, ret = pwm_info(dev, gpio, &ctrl, &line, &id); + int ctrl, id, ret = pwm_info(dev, &line, &ctrl, &id); if (ret) return ret; -- cgit v1.2.3 From a0b25635515ef5049f93b032a1e37f18b16e0f6f Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 21 Nov 2011 16:41:48 +1000 Subject: drm/nouveau/gpio: reimplement as nouveau_gpio.c, fixing a number of issues - moves out of nouveau_bios.c and demagics the logical state definitions - simplifies chipset-specific driver interface - makes most of gpio irq handling common, will use for nv4x hpd later - api extended to allow both direct gpio access, and access using the logical function states - api extended to allow for future use of gpio extender chips - pre-nv50 was handled very badly, the main issue being that all GPIOs were being treated as output-only. - fixes nvd0 so gpio changes actually stick, magic reg needs bashing Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 +- drivers/gpu/drm/nouveau/nouveau_bios.c | 206 +------------- drivers/gpu/drm/nouveau/nouveau_bios.h | 17 -- drivers/gpu/drm/nouveau/nouveau_connector.c | 24 +- drivers/gpu/drm/nouveau/nouveau_dp.c | 7 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 47 ++-- drivers/gpu/drm/nouveau/nouveau_gpio.c | 400 ++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nouveau_gpio.h | 71 +++++ drivers/gpu/drm/nouveau/nouveau_pm.c | 43 ++- drivers/gpu/drm/nouveau/nouveau_state.c | 64 ++--- drivers/gpu/drm/nouveau/nouveau_volt.c | 9 +- drivers/gpu/drm/nouveau/nv04_dac.c | 14 +- drivers/gpu/drm/nouveau/nv10_gpio.c | 94 +++---- drivers/gpu/drm/nouveau/nv17_tv.c | 20 +- drivers/gpu/drm/nouveau/nv50_display.c | 9 +- drivers/gpu/drm/nouveau/nv50_gpio.c | 272 +++---------------- 16 files changed, 653 insertions(+), 646 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nouveau_gpio.c create mode 100644 drivers/gpu/drm/nouveau/nouveau_gpio.h (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index c152a03d287f..9f27e3d9e69a 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -11,7 +11,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ nouveau_hdmi.o nouveau_dp.o nouveau_ramht.o \ nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \ - nouveau_mm.o nouveau_vm.o nouveau_mxm.o \ + nouveau_mm.o nouveau_vm.o nouveau_mxm.o nouveau_gpio.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index f8df37285ba7..e5cbead85e50 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -27,6 +27,7 @@ #include "nouveau_drv.h" #include "nouveau_hw.h" #include "nouveau_encoder.h" +#include "nouveau_gpio.h" #include @@ -3124,49 +3125,6 @@ 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) { @@ -3179,35 +3137,8 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) * each GPIO according to various values listed in each entry */ - struct drm_nouveau_private *dev_priv = bios->dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - int i; - - if (dev_priv->card_type < NV_50) { - NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n"); - return 1; - } - - if (!iexec->execute) - return 1; - - for (i = 0; i < bios->dcb.gpio.entries; i++) { - struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i]; - - 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) - 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); - } + if (iexec->execute && bios->execute) + nouveau_gpio_reset(bios->dev); return 1; } @@ -5643,132 +5574,6 @@ static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) return 0; } -static struct dcb_gpio_entry * -new_gpio_entry(struct nvbios *bios) -{ - struct drm_device *dev = bios->dev; - struct dcb_gpio_table *gpio = &bios->dcb.gpio; - - if (gpio->entries >= DCB_MAX_NUM_GPIO_ENTRIES) { - NV_ERROR(dev, "exceeded maximum number of gpio entries!!\n"); - return NULL; - } - - return &gpio->entry[gpio->entries++]; -} - -struct dcb_gpio_entry * -nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - int i; - - for (i = 0; i < bios->dcb.gpio.entries; i++) { - if (bios->dcb.gpio.entry[i].tag != tag) - continue; - - return &bios->dcb.gpio.entry[i]; - } - - return NULL; -} - -static void -parse_dcb_gpio_table(struct nvbios *bios) -{ - struct drm_device *dev = bios->dev; - struct dcb_gpio_entry *e; - u8 headerlen, entries, recordlen; - u8 *dcb, *gpio = NULL, *entry; - int i; - - dcb = ROMPTR(dev, bios->data[0x36]); - if (dcb[0] >= 0x30) { - gpio = ROMPTR(dev, dcb[10]); - if (!gpio) - goto no_table; - - headerlen = gpio[1]; - entries = gpio[2]; - recordlen = gpio[3]; - } else - if (dcb[0] >= 0x22 && dcb[-1] >= 0x13) { - gpio = ROMPTR(dev, dcb[-15]); - if (!gpio) - goto no_table; - - headerlen = 3; - entries = gpio[2]; - recordlen = gpio[1]; - } else - if (dcb[0] >= 0x22) { - /* No GPIO table present, parse the TVDAC GPIO data. */ - uint8_t *tvdac_gpio = &dcb[-5]; - - if (tvdac_gpio[0] & 1) { - e = new_gpio_entry(bios); - e->tag = DCB_GPIO_TVDAC0; - e->line = tvdac_gpio[1] >> 4; - e->state[0] = !!(tvdac_gpio[0] & 2); - e->state[1] = !e->state[0]; - } - - goto no_table; - } else { - NV_DEBUG(dev, "no/unknown gpio table on DCB 0x%02x\n", dcb[0]); - goto no_table; - } - - entry = gpio + headerlen; - for (i = 0; i < entries; i++, entry += recordlen) { - e = new_gpio_entry(bios); - if (!e) - break; - - if (gpio[0] < 0x40) { - e->entry = ROM16(entry[0]); - e->tag = (e->entry & 0x07e0) >> 5; - if (e->tag == 0x3f) { - bios->dcb.gpio.entries--; - continue; - } - - e->line = (e->entry & 0x001f); - e->state[0] = ((e->entry & 0xf800) >> 11) != 4; - e->state[1] = !e->state[0]; - } else { - e->entry = ROM32(entry[0]); - e->tag = (e->entry & 0x0000ff00) >> 8; - if (e->tag == 0xff) { - bios->dcb.gpio.entries--; - continue; - } - - e->line = (e->entry & 0x0000001f) >> 0; - 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; - } - } - } - -no_table: - /* Apple iMac G4 NV18 */ - if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) { - e = new_gpio_entry(bios); - if (e) { - e->tag = DCB_GPIO_TVDAC0; - e->line = 4; - } - } -} - void * dcb_table(struct drm_device *dev) { @@ -6366,9 +6171,6 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) NV_TRACE(dev, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf); dcb->version = dcbt[0]; - if (dcb->version >= 0x30) - dcb->gpio_table_ptr = ROM16(dcbt[10]); - dcb_outp_foreach(dev, NULL, parse_dcb_entry); /* @@ -6393,8 +6195,6 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) } } dcb_fake_connectors(bios); - - parse_dcb_gpio_table(bios); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 32b911d38e9a..1e382ad5a2b8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -61,19 +61,6 @@ enum dcb_gpio_tag { DCB_GPIO_UNUSED = 0xff }; -struct dcb_gpio_entry { - enum dcb_gpio_tag tag; - int line; - uint32_t entry; - uint8_t state_default; - uint8_t state[2]; -}; - -struct dcb_gpio_table { - int entries; - struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES]; -}; - enum dcb_connector_type { DCB_CONNECTOR_VGA = 0x00, DCB_CONNECTOR_TV_0 = 0x10, @@ -142,12 +129,8 @@ struct dcb_entry { struct dcb_table { uint8_t version; - int entries; struct dcb_entry entry[DCB_MAX_NUM_ENTRIES]; - - uint16_t gpio_table_ptr; - struct dcb_gpio_table gpio; }; enum nouveau_or { diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index df99c7f1191a..f3ce34be082a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -35,6 +35,7 @@ #include "nouveau_encoder.h" #include "nouveau_crtc.h" #include "nouveau_connector.h" +#include "nouveau_gpio.h" #include "nouveau_hw.h" static void nouveau_connector_hotplug(void *, int); @@ -83,7 +84,6 @@ nouveau_connector_destroy(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); struct drm_nouveau_private *dev_priv; - struct nouveau_gpio_engine *pgpio; struct drm_device *dev; if (!nv_connector) @@ -93,10 +93,9 @@ nouveau_connector_destroy(struct drm_connector *connector) dev_priv = dev->dev_private; NV_DEBUG_KMS(dev, "\n"); - pgpio = &dev_priv->engine.gpio; - if (pgpio->irq_unregister) { - pgpio->irq_unregister(dev, nv_connector->hpd, - nouveau_connector_hotplug, connector); + if (nv_connector->hpd != DCB_GPIO_UNUSED) { + nouveau_gpio_isr_del(dev, 0, nv_connector->hpd, 0xff, + nouveau_connector_hotplug, connector); } kfree(nv_connector->edid); @@ -876,7 +875,6 @@ nouveau_connector_create(struct drm_device *dev, int index) const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_display_engine *disp = &dev_priv->engine.display; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_connector *nv_connector = NULL; struct drm_connector *connector; int type, ret = 0; @@ -1050,13 +1048,13 @@ nouveau_connector_create(struct drm_device *dev, int index) break; } - if (nv_connector->hpd != DCB_GPIO_UNUSED && pgpio->irq_register) { - pgpio->irq_register(dev, nv_connector->hpd, - nouveau_connector_hotplug, connector); - - connector->polled = DRM_CONNECTOR_POLL_HPD; - } else { - connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + if (nv_connector->hpd != DCB_GPIO_UNUSED) { + ret = nouveau_gpio_isr_add(dev, 0, nv_connector->hpd, 0xff, + nouveau_connector_hotplug, + connector); + if (ret == 0) + connector->polled = DRM_CONNECTOR_POLL_HPD; } drm_sysfs_connector_add(connector); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 02b00c827da3..9b93b703ceab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -29,6 +29,7 @@ #include "nouveau_connector.h" #include "nouveau_encoder.h" #include "nouveau_crtc.h" +#include "nouveau_gpio.h" /****************************************************************************** * aux channel util functions @@ -556,8 +557,6 @@ dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) bool nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) { - 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_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_connector *nv_connector = @@ -587,7 +586,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) * 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->hpd, false); + nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, false); /* enable down-spreading, if possible */ if (dp.table[1] >= 16) { @@ -636,7 +635,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc); /* re-enable hotplug detect */ - pgpio->irq_enable(dev, nv_connector->hpd, true); + nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, true); return true; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 909b991416ed..0af525820347 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -408,19 +408,13 @@ struct nouveau_display_engine { }; struct nouveau_gpio_engine { - void *priv; - - int (*init)(struct drm_device *); - void (*takedown)(struct drm_device *); - - int (*get)(struct drm_device *, enum dcb_gpio_tag); - int (*set)(struct drm_device *, enum dcb_gpio_tag, int state); - - int (*irq_register)(struct drm_device *, enum dcb_gpio_tag, - void (*)(void *, int), void *); - void (*irq_unregister)(struct drm_device *, enum dcb_gpio_tag, - void (*)(void *, int), void *); - bool (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on); + spinlock_t lock; + struct list_head isr; + int (*init)(struct drm_device *); + void (*fini)(struct drm_device *); + int (*drive)(struct drm_device *, int line, int dir, int out); + int (*sense)(struct drm_device *, int line); + void (*irq_enable)(struct drm_device *, int line, bool); }; struct nouveau_pm_voltage_level { @@ -1091,8 +1085,6 @@ 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 * nouveau_bios_connector_entry(struct drm_device *, int index); extern u32 get_pll_register(struct drm_device *, enum pll_types); @@ -1476,23 +1468,22 @@ int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *, uint32_t handle); /* nv10_gpio.c */ -int nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); -int nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); +int nv10_gpio_init(struct drm_device *dev); +void nv10_gpio_fini(struct drm_device *dev); +int nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out); +int nv10_gpio_sense(struct drm_device *dev, int line); +void nv10_gpio_irq_enable(struct drm_device *, int line, bool on); /* nv50_gpio.c */ 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, - void (*)(void *, int), void *); -bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on); - -/* nv50_calc. */ +int nv50_gpio_drive(struct drm_device *dev, int line, int dir, int out); +int nv50_gpio_sense(struct drm_device *dev, int line); +void nv50_gpio_irq_enable(struct drm_device *, int line, bool on); +int nvd0_gpio_drive(struct drm_device *dev, int line, int dir, int out); +int nvd0_gpio_sense(struct drm_device *dev, int line); + +/* nv50_calc.c */ int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, int *N1, int *M1, int *N2, int *M2, int *P); int nva3_calc_pll(struct drm_device *, struct pll_lims *, diff --git a/drivers/gpu/drm/nouveau/nouveau_gpio.c b/drivers/gpu/drm/nouveau/nouveau_gpio.c new file mode 100644 index 000000000000..a580cc62337a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_gpio.c @@ -0,0 +1,400 @@ +/* + * 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_i2c.h" +#include "nouveau_gpio.h" + +static u8 * +dcb_gpio_table(struct drm_device *dev) +{ + u8 *dcb = dcb_table(dev); + if (dcb) { + if (dcb[0] >= 0x30 && dcb[1] >= 0x0c) + return ROMPTR(dev, dcb[0x0a]); + if (dcb[0] >= 0x22 && dcb[-1] >= 0x13) + return ROMPTR(dev, dcb[-15]); + } + return NULL; +} + +static u8 * +dcb_gpio_entry(struct drm_device *dev, int idx, int ent, u8 *version) +{ + u8 *table = dcb_gpio_table(dev); + if (table) { + *version = table[0]; + if (*version < 0x30 && ent < table[2]) + return table + 3 + (ent * table[1]); + else if (ent < table[2]) + return table + table[1] + (ent * table[3]); + } + return NULL; +} + +int +nouveau_gpio_drive(struct drm_device *dev, int idx, int line, int dir, int out) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + return pgpio->drive ? pgpio->drive(dev, line, dir, out) : -ENODEV; +} + +int +nouveau_gpio_sense(struct drm_device *dev, int idx, int line) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + return pgpio->sense ? pgpio->sense(dev, line) : -ENODEV; +} + +int +nouveau_gpio_find(struct drm_device *dev, int idx, u8 func, u8 line, + struct gpio_func *gpio) +{ + u8 *table, *entry, version; + int i = -1; + + if (line == 0xff && func == 0xff) + return -EINVAL; + + while ((entry = dcb_gpio_entry(dev, idx, ++i, &version))) { + if (version < 0x40) { + u16 data = ROM16(entry[0]); + *gpio = (struct gpio_func) { + .line = (data & 0x001f) >> 0, + .func = (data & 0x07e0) >> 5, + .log[0] = (data & 0x1800) >> 11, + .log[1] = (data & 0x6000) >> 13, + }; + } else + if (version < 0x41) { + *gpio = (struct gpio_func) { + .line = entry[0] & 0x1f, + .func = entry[1], + .log[0] = (entry[3] & 0x18) >> 3, + .log[1] = (entry[3] & 0x60) >> 5, + }; + } else { + *gpio = (struct gpio_func) { + .line = entry[0] & 0x3f, + .func = entry[1], + .log[0] = (entry[4] & 0x30) >> 4, + .log[1] = (entry[4] & 0xc0) >> 6, + }; + } + + if ((line == 0xff || line == gpio->line) && + (func == 0xff || func == gpio->func)) + return 0; + } + + /* DCB 2.2, fixed TVDAC GPIO data */ + if ((table = dcb_table(dev)) && table[0] >= 0x22) { + if (func == DCB_GPIO_TVDAC0) { + *gpio = (struct gpio_func) { + .func = DCB_GPIO_TVDAC0, + .line = table[-4] >> 4, + .log[0] = !!(table[-5] & 2), + .log[1] = !(table[-5] & 2), + }; + return 0; + } + } + + /* Apple iMac G4 NV18 */ + if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) { + if (func == DCB_GPIO_TVDAC0) { + *gpio = (struct gpio_func) { + .func = DCB_GPIO_TVDAC0, + .line = 4, + .log[0] = 0, + .log[1] = 1, + }; + return 0; + } + } + + return -EINVAL; +} + +int +nouveau_gpio_set(struct drm_device *dev, int idx, u8 tag, u8 line, int state) +{ + struct gpio_func gpio; + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); + if (ret == 0) { + int dir = !!(gpio.log[state] & 0x02); + int out = !!(gpio.log[state] & 0x01); + ret = nouveau_gpio_drive(dev, idx, gpio.line, dir, out); + } + + return ret; +} + +int +nouveau_gpio_get(struct drm_device *dev, int idx, u8 tag, u8 line) +{ + struct gpio_func gpio; + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); + if (ret == 0) { + ret = nouveau_gpio_sense(dev, idx, gpio.line); + if (ret >= 0) + ret = (ret == (gpio.log[1] & 1)); + } + + return ret; +} + +int +nouveau_gpio_irq(struct drm_device *dev, int idx, u8 tag, u8 line, bool on) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_func gpio; + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); + if (ret == 0) { + if (idx == 0 && pgpio->irq_enable) + pgpio->irq_enable(dev, gpio.line, on); + else + ret = -ENODEV; + } + + return ret; +} + +struct gpio_isr { + struct drm_device *dev; + struct list_head head; + struct work_struct work; + int idx; + struct gpio_func func; + void (*handler)(void *, int); + void *data; + bool inhibit; +}; + +static void +nouveau_gpio_isr_bh(struct work_struct *work) +{ + struct gpio_isr *isr = container_of(work, struct gpio_isr, work); + struct drm_device *dev = isr->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + unsigned long flags; + int state; + + state = nouveau_gpio_get(dev, isr->idx, isr->func.func, isr->func.line); + if (state >= 0) + isr->handler(isr->data, state); + + spin_lock_irqsave(&pgpio->lock, flags); + isr->inhibit = false; + spin_unlock_irqrestore(&pgpio->lock, flags); +} + +void +nouveau_gpio_isr(struct drm_device *dev, int idx, u32 line_mask) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_isr *isr; + + if (idx != 0) + return; + + spin_lock(&pgpio->lock); + list_for_each_entry(isr, &pgpio->isr, head) { + if (line_mask & (1 << isr->func.line)) { + if (isr->inhibit) + continue; + isr->inhibit = true; + schedule_work(&isr->work); + } + } + spin_unlock(&pgpio->lock); +} + +int +nouveau_gpio_isr_add(struct drm_device *dev, int idx, u8 tag, u8 line, + void (*handler)(void *, int), void *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_isr *isr; + unsigned long flags; + int ret; + + isr = kzalloc(sizeof(*isr), GFP_KERNEL); + if (!isr) + return -ENOMEM; + + ret = nouveau_gpio_find(dev, idx, tag, line, &isr->func); + if (ret) { + kfree(isr); + return ret; + } + + INIT_WORK(&isr->work, nouveau_gpio_isr_bh); + isr->dev = dev; + isr->handler = handler; + isr->data = data; + isr->idx = idx; + + spin_lock_irqsave(&pgpio->lock, flags); + list_add(&isr->head, &pgpio->isr); + spin_unlock_irqrestore(&pgpio->lock, flags); + return 0; +} + +void +nouveau_gpio_isr_del(struct drm_device *dev, int idx, u8 tag, u8 line, + void (*handler)(void *, int), void *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct gpio_isr *isr, *tmp; + struct gpio_func func; + unsigned long flags; + LIST_HEAD(tofree); + int ret; + + ret = nouveau_gpio_find(dev, idx, tag, line, &func); + if (ret == 0) { + spin_lock_irqsave(&pgpio->lock, flags); + list_for_each_entry_safe(isr, tmp, &pgpio->isr, head) { + if (memcmp(&isr->func, &func, sizeof(func)) || + isr->idx != idx || + isr->handler != handler || isr->data != data) + continue; + list_move(&isr->head, &tofree); + } + spin_unlock_irqrestore(&pgpio->lock, flags); + + list_for_each_entry_safe(isr, tmp, &tofree, head) { + flush_work_sync(&isr->work); + kfree(isr); + } + } +} + +int +nouveau_gpio_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + INIT_LIST_HEAD(&pgpio->isr); + spin_lock_init(&pgpio->lock); + + return nouveau_gpio_init(dev); +} + +void +nouveau_gpio_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + nouveau_gpio_fini(dev); + BUG_ON(!list_empty(&pgpio->isr)); +} + +int +nouveau_gpio_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + int ret = 0; + + if (pgpio->init) + ret = pgpio->init(dev); + + return ret; +} + +void +nouveau_gpio_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + + if (pgpio->fini) + pgpio->fini(dev); +} + +void +nouveau_gpio_reset(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u8 *entry, version; + int ent = -1; + + while ((entry = dcb_gpio_entry(dev, 0, ++ent, &version))) { + u8 func = 0xff, line, defs, unk0, unk1; + if (version >= 0x41) { + defs = !!(entry[0] & 0x80); + line = entry[0] & 0x3f; + func = entry[1]; + unk0 = entry[2]; + unk1 = entry[3] & 0x1f; + } else + if (version >= 0x40) { + line = entry[0] & 0x1f; + func = entry[1]; + defs = !!(entry[3] & 0x01); + unk0 = !!(entry[3] & 0x02); + unk1 = !!(entry[3] & 0x04); + } else { + break; + } + + if (func == 0xff) + continue; + + nouveau_gpio_func_set(dev, func, defs); + + if (dev_priv->card_type >= NV_D0) { + nv_mask(dev, 0x00d610 + (line * 4), 0xff, unk0); + if (unk1--) + nv_mask(dev, 0x00d640 + (unk1 * 4), 0xff, line); + } else + if (dev_priv->card_type >= NV_50) { + static const u32 regs[] = { 0xe100, 0xe28c }; + u32 val = (unk1 << 16) | unk0; + u32 reg = regs[line >> 4]; line &= 0x0f; + + nv_mask(dev, reg, 0x00010001 << line, val << line); + } + } +} diff --git a/drivers/gpu/drm/nouveau/nouveau_gpio.h b/drivers/gpu/drm/nouveau/nouveau_gpio.h new file mode 100644 index 000000000000..64c5cb077ace --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_gpio.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef __NOUVEAU_GPIO_H__ +#define __NOUVEAU_GPIO_H__ + +struct gpio_func { + u8 func; + u8 line; + u8 log[2]; +}; + +/* nouveau_gpio.c */ +int nouveau_gpio_create(struct drm_device *); +void nouveau_gpio_destroy(struct drm_device *); +int nouveau_gpio_init(struct drm_device *); +void nouveau_gpio_fini(struct drm_device *); +void nouveau_gpio_reset(struct drm_device *); +int nouveau_gpio_drive(struct drm_device *, int idx, int line, + int dir, int out); +int nouveau_gpio_sense(struct drm_device *, int idx, int line); +int nouveau_gpio_find(struct drm_device *, int idx, u8 tag, u8 line, + struct gpio_func *); +int nouveau_gpio_set(struct drm_device *, int idx, u8 tag, u8 line, int state); +int nouveau_gpio_get(struct drm_device *, int idx, u8 tag, u8 line); +int nouveau_gpio_irq(struct drm_device *, int idx, u8 tag, u8 line, bool on); +void nouveau_gpio_isr(struct drm_device *, int idx, u32 mask); +int nouveau_gpio_isr_add(struct drm_device *, int idx, u8 tag, u8 line, + void (*)(void *, int state), void *data); +void nouveau_gpio_isr_del(struct drm_device *, int idx, u8 tag, u8 line, + void (*)(void *, int state), void *data); + +static inline bool +nouveau_gpio_func_valid(struct drm_device *dev, u8 tag) +{ + struct gpio_func func; + return (nouveau_gpio_find(dev, 0, tag, 0xff, &func)) == 0; +} + +static inline int +nouveau_gpio_func_set(struct drm_device *dev, u8 tag, int state) +{ + return nouveau_gpio_set(dev, 0, tag, 0xff, state); +} + +static inline int +nouveau_gpio_func_get(struct drm_device *dev, u8 tag) +{ + return nouveau_gpio_get(dev, 0, tag, 0xff); +} + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 788ba33da77c..aba3362d421a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -26,6 +26,7 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" +#include "nouveau_gpio.h" #ifdef CONFIG_ACPI #include @@ -38,27 +39,25 @@ static int nouveau_pwmfan_get(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct dcb_gpio_entry *gpio = NULL; + struct gpio_func gpio; u32 divs, duty; int ret; if (!pm->pwm_get) return -ENODEV; - gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN); - if (gpio) { - ret = pm->pwm_get(dev, gpio->line, &divs, &duty); + ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); + if (ret == 0) { + ret = pm->pwm_get(dev, gpio.line, &divs, &duty); if (ret == 0) { divs = max(divs, duty); - if (dev_priv->card_type <= NV_40 || - (gpio->state[0] & 1)) + if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) duty = divs - duty; return (duty * 100) / divs; } - return pgpio->get(dev, gpio->tag) * 100; + return nouveau_gpio_func_get(dev, gpio.func) * 100; } return -ENODEV; @@ -69,14 +68,15 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct dcb_gpio_entry *gpio; + struct gpio_func gpio; u32 divs, duty; + int ret; if (!pm->pwm_set) return -ENODEV; - gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN); - if (gpio) { + ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); + if (ret == 0) { divs = pm->pwm_divisor; if (pm->fan.pwm_freq) { /*XXX: PNVIO clock more than likely... */ @@ -86,11 +86,10 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) } duty = ((divs * percent) + 99) / 100; - if (dev_priv->card_type <= NV_40 || - (gpio->state[0] & 1)) + if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) duty = divs - duty; - return pm->pwm_set(dev, gpio->line, divs, duty); + return pm->pwm_set(dev, gpio.line, divs, duty); } return -ENODEV; @@ -472,24 +471,24 @@ nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr, struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct dcb_gpio_entry *gpio; + struct gpio_func gpio; u32 cycles, cur, prev; u64 start; + int ret; - gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE); - if (!gpio) - return -ENODEV; + ret = nouveau_gpio_find(dev, 0, DCB_GPIO_FAN_SENSE, 0xff, &gpio); + if (ret) + return ret; /* Monitor the GPIO input 0x3b for 250ms. * When the fan spins, it changes the value of GPIO FAN_SENSE. * We get 4 changes (0 -> 1 -> 0 -> 1 -> [...]) per complete rotation. */ start = ptimer->read(dev); - prev = pgpio->get(dev, DCB_GPIO_FAN_SENSE); + prev = nouveau_gpio_sense(dev, 0, gpio.line); cycles = 0; do { - cur = pgpio->get(dev, DCB_GPIO_FAN_SENSE); + cur = nouveau_gpio_sense(dev, 0, gpio.line); if (prev != cur) { cycles++; prev = cur; @@ -701,7 +700,7 @@ nouveau_hwmon_init(struct drm_device *dev) } /* if the card can read the fan rpm */ - if (nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE)) { + if (nouveau_gpio_func_valid(dev, DCB_GPIO_FAN_SENSE)) { ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup); if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 5d8ad4ec3ac1..c4edba6a457d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -36,6 +36,7 @@ #include "nouveau_drm.h" #include "nouveau_fbcon.h" #include "nouveau_ramht.h" +#include "nouveau_gpio.h" #include "nouveau_pm.h" #include "nv50_display.h" @@ -83,11 +84,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.destroy = nv04_display_destroy; engine->display.init = nv04_display_init; engine->display.fini = nv04_display_fini; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = NULL; - engine->gpio.set = NULL; - engine->gpio.irq_enable = NULL; engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; @@ -133,11 +129,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.destroy = nv04_display_destroy; engine->display.init = nv04_display_init; engine->display.fini = nv04_display_fini; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; @@ -183,11 +176,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.destroy = nv04_display_destroy; engine->display.init = nv04_display_init; engine->display.fini = nv04_display_fini; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; @@ -233,11 +223,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.destroy = nv04_display_destroy; engine->display.init = nv04_display_init; engine->display.fini = nv04_display_fini; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; @@ -286,11 +273,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.destroy = nv04_display_destroy; engine->display.init = nv04_display_init; engine->display.fini = nv04_display_fini; - engine->gpio.init = nouveau_stub_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv10_gpio_get; - engine->gpio.set = nv10_gpio_set; - engine->gpio.irq_enable = NULL; + engine->gpio.drive = nv10_gpio_drive; + engine->gpio.sense = nv10_gpio_sense; engine->pm.clocks_get = nv40_pm_clocks_get; engine->pm.clocks_pre = nv40_pm_clocks_pre; engine->pm.clocks_set = nv40_pm_clocks_set; @@ -345,11 +329,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.init = nv50_display_init; engine->display.fini = nv50_display_fini; engine->gpio.init = nv50_gpio_init; - engine->gpio.takedown = nv50_gpio_fini; - engine->gpio.get = nv50_gpio_get; - engine->gpio.set = nv50_gpio_set; - engine->gpio.irq_register = nv50_gpio_irq_register; - engine->gpio.irq_unregister = nv50_gpio_irq_unregister; + engine->gpio.fini = nv50_gpio_fini; + engine->gpio.drive = nv50_gpio_drive; + engine->gpio.sense = nv50_gpio_sense; engine->gpio.irq_enable = nv50_gpio_irq_enable; switch (dev_priv->chipset) { case 0x84: @@ -421,11 +403,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.init = nv50_display_init; engine->display.fini = nv50_display_fini; engine->gpio.init = nv50_gpio_init; - engine->gpio.takedown = nouveau_stub_takedown; - engine->gpio.get = nv50_gpio_get; - engine->gpio.set = nv50_gpio_set; - engine->gpio.irq_register = nv50_gpio_irq_register; - engine->gpio.irq_unregister = nv50_gpio_irq_unregister; + engine->gpio.fini = nv50_gpio_fini; + engine->gpio.drive = nv50_gpio_drive; + engine->gpio.sense = nv50_gpio_sense; engine->gpio.irq_enable = nv50_gpio_irq_enable; engine->vram.init = nvc0_vram_init; engine->vram.takedown = nv50_vram_fini; @@ -474,11 +454,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.init = nvd0_display_init; engine->display.fini = nvd0_display_fini; 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.fini = nv50_gpio_fini; + engine->gpio.drive = nvd0_gpio_drive; + engine->gpio.sense = nvd0_gpio_sense; engine->gpio.irq_enable = nv50_gpio_irq_enable; engine->vram.init = nvc0_vram_init; engine->vram.takedown = nv50_vram_fini; @@ -630,7 +608,7 @@ nouveau_card_init(struct drm_device *dev) goto out_gart; /* PGPIO */ - ret = engine->gpio.init(dev); + ret = nouveau_gpio_create(dev); if (ret) goto out_mc; @@ -798,7 +776,7 @@ out_engine: out_timer: engine->timer.takedown(dev); out_gpio: - engine->gpio.takedown(dev); + nouveau_gpio_destroy(dev); out_mc: engine->mc.takedown(dev); out_gart: @@ -851,7 +829,7 @@ static void nouveau_card_takedown(struct drm_device *dev) } engine->fb.takedown(dev); engine->timer.takedown(dev); - engine->gpio.takedown(dev); + nouveau_gpio_destroy(dev); engine->mc.takedown(dev); engine->display.late_takedown(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index ac15b46ea3a0..b010cb997b34 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -26,6 +26,7 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" +#include "nouveau_gpio.h" static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 }; static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]); @@ -34,7 +35,6 @@ int nouveau_voltage_gpio_get(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; u8 vid = 0; int i; @@ -43,7 +43,7 @@ nouveau_voltage_gpio_get(struct drm_device *dev) if (!(volt->vid_mask & (1 << i))) continue; - vid |= gpio->get(dev, vidtag[i]) << i; + vid |= nouveau_gpio_func_get(dev, vidtag[i]) << i; } return nouveau_volt_lvl_lookup(dev, vid); @@ -53,7 +53,6 @@ int nouveau_voltage_gpio_set(struct drm_device *dev, int voltage) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; int vid, i; @@ -65,7 +64,7 @@ nouveau_voltage_gpio_set(struct drm_device *dev, int voltage) if (!(volt->vid_mask & (1 << i))) continue; - gpio->set(dev, vidtag[i], !!(vid & (1 << i))); + nouveau_gpio_func_set(dev, vidtag[i], !!(vid & (1 << i))); } return 0; @@ -194,7 +193,7 @@ nouveau_volt_init(struct drm_device *dev) return; } - if (!nouveau_bios_gpio_entry(dev, vidtag[i])) { + if (!nouveau_gpio_func_valid(dev, vidtag[i])) { NV_DEBUG(dev, "vid bit %d has no gpio tag\n", i); return; } diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c index e000455e06d0..8300266ffaea 100644 --- a/drivers/gpu/drm/nouveau/nv04_dac.c +++ b/drivers/gpu/drm/nouveau/nv04_dac.c @@ -32,6 +32,7 @@ #include "nouveau_connector.h" #include "nouveau_crtc.h" #include "nouveau_hw.h" +#include "nouveau_gpio.h" #include "nvreg.h" int nv04_dac_output_offset(struct drm_encoder *encoder) @@ -220,7 +221,6 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, @@ -252,11 +252,11 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); } - saved_gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1); - saved_gpio0 = gpio->get(dev, DCB_GPIO_TVDAC0); + saved_gpio1 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC1); + saved_gpio0 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC0); - gpio->set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); - gpio->set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); msleep(4); @@ -306,8 +306,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); - gpio->set(dev, DCB_GPIO_TVDAC1, saved_gpio1); - gpio->set(dev, DCB_GPIO_TVDAC0, saved_gpio0); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); return sample; } diff --git a/drivers/gpu/drm/nouveau/nv10_gpio.c b/drivers/gpu/drm/nouveau/nv10_gpio.c index 748c9f739116..419d6495649b 100644 --- a/drivers/gpu/drm/nouveau/nv10_gpio.c +++ b/drivers/gpu/drm/nouveau/nv10_gpio.c @@ -28,65 +28,55 @@ #include "nouveau_drv.h" #include "nouveau_hw.h" -static bool -get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift, - uint32_t *mask) -{ - if (ent->line < 2) { - *reg = NV_PCRTC_GPIO; - *shift = ent->line * 16; - *mask = 0x11; - - } else if (ent->line < 10) { - *reg = NV_PCRTC_GPIO_EXT; - *shift = (ent->line - 2) * 4; - *mask = 0x3; - - } else if (ent->line < 14) { - *reg = NV_PCRTC_850; - *shift = (ent->line - 10) * 4; - *mask = 0x3; - - } else { - return false; - } - - return true; -} - int -nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +nv10_gpio_sense(struct drm_device *dev, int line) { - struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag); - uint32_t reg, shift, mask, value; - - if (!ent) - return -ENODEV; - - if (!get_gpio_location(ent, ®, &shift, &mask)) - return -ENODEV; - - value = NVReadCRTC(dev, 0, reg) >> shift; + if (line < 2) { + line = line * 16; + line = NVReadCRTC(dev, 0, NV_PCRTC_GPIO) >> line; + return !!(line & 0x0100); + } else + if (line < 10) { + line = (line - 2) * 4; + line = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT) >> line; + return !!(line & 0x04); + } else + if (line < 14) { + line = (line - 10) * 4; + line = NVReadCRTC(dev, 0, NV_PCRTC_850) >> line; + return !!(line & 0x04); + } - return (value & 1) == ent->state[1]; + return -EINVAL; } int -nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out) { - struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag); - uint32_t reg, shift, mask, value; - - if (!ent) - return -ENODEV; - - if (!get_gpio_location(ent, ®, &shift, &mask)) - return -ENODEV; - - value = ent->state[state & 1] << shift; - mask = ~(mask << shift); - - NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask)); + u32 reg, mask, data; + + if (line < 2) { + line = line * 16; + reg = NV_PCRTC_GPIO; + mask = 0x00000011; + data = (dir << 4) | out; + } else + if (line < 10) { + line = (line - 2) * 4; + reg = NV_PCRTC_GPIO_EXT; + mask = 0x00000003 << ((line - 2) * 4); + data = (dir << 1) | out; + } else + if (line < 14) { + line = (line - 10) * 4; + reg = NV_PCRTC_850; + mask = 0x00000003; + data = (dir << 1) | out; + } else { + return -EINVAL; + } + mask = NVReadCRTC(dev, 0, reg) & ~(mask << line); + NVWriteCRTC(dev, 0, reg, mask | (data << line)); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 3900cebba560..696d7e7dc2a0 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -30,6 +30,7 @@ #include "nouveau_encoder.h" #include "nouveau_connector.h" #include "nouveau_crtc.h" +#include "nouveau_gpio.h" #include "nouveau_hw.h" #include "nv17_tv.h" @@ -37,7 +38,6 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; uint32_t testval, regoffset = nv04_dac_output_offset(encoder); uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end, fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c; @@ -53,8 +53,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) head = (dacclk & 0x100) >> 8; /* Save the previous state. */ - gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1); - gpio0 = gpio->get(dev, DCB_GPIO_TVDAC0); + gpio1 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC1); + gpio0 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC0); fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL); fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START); fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END); @@ -65,8 +65,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c); /* Prepare the DAC for load detection. */ - gpio->set(dev, DCB_GPIO_TVDAC1, true); - gpio->set(dev, DCB_GPIO_TVDAC0, true); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, true); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, true); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047); @@ -111,8 +111,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start); NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal); - gpio->set(dev, DCB_GPIO_TVDAC1, gpio1); - gpio->set(dev, DCB_GPIO_TVDAC0, gpio0); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, gpio1); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, gpio0); return sample; } @@ -357,8 +357,6 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); @@ -383,8 +381,8 @@ static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) nv_load_ptv(dev, regs, 200); - gpio->set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON); - gpio->set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON); + nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON); nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 9708b94a0a7b..f408e105a0cd 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -32,6 +32,7 @@ #include "nouveau_fb.h" #include "nouveau_fbcon.h" #include "nouveau_ramht.h" +#include "nouveau_gpio.h" #include "drm_crtc_helper.h" static void nv50_display_isr(struct drm_device *); @@ -140,8 +141,6 @@ nv50_display_sync(struct drm_device *dev) int nv50_display_init(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct drm_connector *connector; struct nouveau_channel *evo; int ret, i; @@ -240,11 +239,7 @@ nv50_display_init(struct drm_device *dev) /* enable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - - if (conn->hpd == DCB_GPIO_UNUSED) - continue; - - pgpio->irq_enable(dev, conn->hpd, true); + nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); } ret = nv50_evo_init(dev); diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c index 793a5ccca121..f429e6a8ca7a 100644 --- a/drivers/gpu/drm/nouveau/nv50_gpio.c +++ b/drivers/gpu/drm/nouveau/nv50_gpio.c @@ -25,229 +25,95 @@ #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_hw.h" +#include "nouveau_gpio.h" #include "nv50_display.h" -static void nv50_gpio_isr(struct drm_device *dev); -static void nv50_gpio_isr_bh(struct work_struct *work); - -struct nv50_gpio_priv { - struct list_head handlers; - spinlock_t lock; -}; - -struct nv50_gpio_handler { - struct drm_device *dev; - struct list_head head; - struct work_struct work; - bool inhibit; - - struct dcb_gpio_entry *gpio; - - void (*handler)(void *data, int state); - void *data; -}; - static int -nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift) +nv50_gpio_location(int line, u32 *reg, u32 *shift) { const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; - if (gpio->line >= 32) + if (line >= 32) return -EINVAL; - *reg = nv50_gpio_reg[gpio->line >> 3]; - *shift = (gpio->line & 7) << 2; + *reg = nv50_gpio_reg[line >> 3]; + *shift = (line & 7) << 2; return 0; } int -nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +nv50_gpio_drive(struct drm_device *dev, int line, int dir, int out) { - struct dcb_gpio_entry *gpio; - uint32_t r, s, v; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; + u32 reg, shift; - if (nv50_gpio_location(gpio, &r, &s)) + if (nv50_gpio_location(line, ®, &shift)) return -EINVAL; - v = nv_rd32(dev, r) >> (s + 2); - return ((v & 1) == (gpio->state[1] & 1)); + nv_mask(dev, reg, 7 << shift, (((dir ^ 1) << 1) | out) << shift); + return 0; } int -nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +nv50_gpio_sense(struct drm_device *dev, int line) { - struct dcb_gpio_entry *gpio; - uint32_t r, s, v; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; + u32 reg, shift; - if (nv50_gpio_location(gpio, &r, &s)) + if (nv50_gpio_location(line, ®, &shift)) return -EINVAL; - v = nv_rd32(dev, r) & ~(0x3 << s); - v |= (gpio->state[state] ^ 2) << s; - nv_wr32(dev, r, v); - return 0; + return !!(nv_rd32(dev, reg) & (4 << shift)); } -int -nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +void +nv50_gpio_irq_enable(struct drm_device *dev, int line, bool on) { - struct dcb_gpio_entry *gpio; - u32 v; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; + u32 reg = line < 16 ? 0xe050 : 0xe070; + u32 mask = 0x00010001 << (line & 0xf); - v = nv_rd32(dev, 0x00d610 + (gpio->line * 4)); - v &= 0x00004000; - return (!!v == (gpio->state[1] & 1)); + nv_wr32(dev, reg + 4, mask); + nv_mask(dev, reg + 0, mask, on ? mask : 0); } int -nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +nvd0_gpio_drive(struct drm_device *dev, int line, int dir, int out) { - 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); + u32 data = ((dir ^ 1) << 13) | (out << 12); + nv_mask(dev, 0x00d610 + (line * 4), 0x00003000, data); + nv_mask(dev, 0x00d604, 0x00000001, 0x00000001); /* update? */ return 0; } int -nv50_gpio_irq_register(struct drm_device *dev, enum dcb_gpio_tag tag, - void (*handler)(void *, int), void *data) +nvd0_gpio_sense(struct drm_device *dev, int line) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - struct nv50_gpio_handler *gpioh; - struct dcb_gpio_entry *gpio; - unsigned long flags; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return -ENOENT; - - gpioh = kzalloc(sizeof(*gpioh), GFP_KERNEL); - if (!gpioh) - return -ENOMEM; - - INIT_WORK(&gpioh->work, nv50_gpio_isr_bh); - gpioh->dev = dev; - gpioh->gpio = gpio; - gpioh->handler = handler; - gpioh->data = data; - - spin_lock_irqsave(&priv->lock, flags); - list_add(&gpioh->head, &priv->handlers); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; + return !!(nv_rd32(dev, 0x00d610 + (line * 4)) & 0x00004000); } -void -nv50_gpio_irq_unregister(struct drm_device *dev, enum dcb_gpio_tag tag, - void (*handler)(void *, int), void *data) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - struct nv50_gpio_handler *gpioh, *tmp; - struct dcb_gpio_entry *gpio; - LIST_HEAD(tofree); - unsigned long flags; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return; - - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry_safe(gpioh, tmp, &priv->handlers, head) { - if (gpioh->gpio != gpio || - gpioh->handler != handler || - gpioh->data != data) - continue; - list_move(&gpioh->head, &tofree); - } - spin_unlock_irqrestore(&priv->lock, flags); - - list_for_each_entry_safe(gpioh, tmp, &tofree, head) { - flush_work_sync(&gpioh->work); - kfree(gpioh); - } -} - -bool -nv50_gpio_irq_enable(struct drm_device *dev, enum dcb_gpio_tag tag, bool on) -{ - struct dcb_gpio_entry *gpio; - u32 reg, mask; - - gpio = nouveau_bios_gpio_entry(dev, tag); - if (!gpio) - return false; - - reg = gpio->line < 16 ? 0xe050 : 0xe070; - mask = 0x00010001 << (gpio->line & 0xf); - - nv_wr32(dev, reg + 4, mask); - reg = nv_mask(dev, reg + 0, mask, on ? mask : 0); - return (reg & mask) == mask; -} - -static int -nv50_gpio_create(struct drm_device *dev) +static void +nv50_gpio_isr(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + u32 intr0, intr1 = 0; + u32 hi, lo; - INIT_LIST_HEAD(&priv->handlers); - spin_lock_init(&priv->lock); - pgpio->priv = priv; - return 0; -} + intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050); + if (dev_priv->chipset >= 0x90) + intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070); -static void -nv50_gpio_destroy(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + hi = (intr0 & 0x0000ffff) | (intr1 << 16); + lo = (intr0 >> 16) | (intr1 & 0xffff0000); + nouveau_gpio_isr(dev, 0, hi | lo); - kfree(pgpio->priv); - pgpio->priv = NULL; + nv_wr32(dev, 0xe054, intr0); + if (dev_priv->chipset >= 0x90) + nv_wr32(dev, 0xe074, intr1); } int nv50_gpio_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - int ret; - - if (!pgpio->priv) { - ret = nv50_gpio_create(dev); - if (ret) - return ret; - } /* disable, and ack any pending gpio interrupts */ nv_wr32(dev, 0xe050, 0x00000000); @@ -270,64 +136,4 @@ nv50_gpio_fini(struct drm_device *dev) if (dev_priv->chipset >= 0x90) nv_wr32(dev, 0xe070, 0x00000000); nouveau_irq_unregister(dev, 21); - - nv50_gpio_destroy(dev); -} - -static void -nv50_gpio_isr_bh(struct work_struct *work) -{ - struct nv50_gpio_handler *gpioh = - container_of(work, struct nv50_gpio_handler, work); - struct drm_nouveau_private *dev_priv = gpioh->dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - unsigned long flags; - int state; - - state = pgpio->get(gpioh->dev, gpioh->gpio->tag); - if (state < 0) - return; - - gpioh->handler(gpioh->data, state); - - spin_lock_irqsave(&priv->lock, flags); - gpioh->inhibit = false; - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void -nv50_gpio_isr(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct nv50_gpio_priv *priv = pgpio->priv; - struct nv50_gpio_handler *gpioh; - u32 intr0, intr1 = 0; - u32 hi, lo, ch; - - intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050); - if (dev_priv->chipset >= 0x90) - intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070); - - hi = (intr0 & 0x0000ffff) | (intr1 << 16); - lo = (intr0 >> 16) | (intr1 & 0xffff0000); - ch = hi | lo; - - nv_wr32(dev, 0xe054, intr0); - if (dev_priv->chipset >= 0x90) - nv_wr32(dev, 0xe074, intr1); - - spin_lock(&priv->lock); - list_for_each_entry(gpioh, &priv->handlers, head) { - if (!(ch & (1 << gpioh->gpio->line))) - continue; - - if (gpioh->inhibit) - continue; - gpioh->inhibit = true; - - schedule_work(&gpioh->work); - } - spin_unlock(&priv->lock); } -- cgit v1.2.3 From 47e5d5cb83d4b41168f4afa1ca32843d4a126cc8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 22 Nov 2011 13:49:22 +1000 Subject: drm/nv40/disp: implement support for hotplug irq Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 3 +++ drivers/gpu/drm/nouveau/nv04_display.c | 8 +++++++ drivers/gpu/drm/nouveau/nv10_gpio.c | 41 +++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index c4edba6a457d..57ccda47a70b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -273,8 +273,11 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.destroy = nv04_display_destroy; engine->display.init = nv04_display_init; engine->display.fini = nv04_display_fini; + engine->gpio.init = nv10_gpio_init; + engine->gpio.fini = nv10_gpio_fini; engine->gpio.drive = nv10_gpio_drive; engine->gpio.sense = nv10_gpio_sense; + engine->gpio.irq_enable = nv10_gpio_irq_enable; engine->pm.clocks_get = nv40_pm_clocks_get; engine->pm.clocks_pre = nv40_pm_clocks_pre; engine->pm.clocks_set = nv40_pm_clocks_set; diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 7047d37e8dab..15b748f0ea4b 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -31,6 +31,7 @@ #include "nouveau_hw.h" #include "nouveau_encoder.h" #include "nouveau_connector.h" +#include "nouveau_gpio.h" static void nv04_vblank_crtc0_isr(struct drm_device *); static void nv04_vblank_crtc1_isr(struct drm_device *); @@ -220,6 +221,7 @@ nv04_display_destroy(struct drm_device *dev) int nv04_display_init(struct drm_device *dev) { + struct drm_connector *connector; struct drm_encoder *encoder; struct drm_crtc *crtc; @@ -240,6 +242,12 @@ nv04_display_init(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) crtc->funcs->restore(crtc); + /* enable hotplug interrupts */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct nouveau_connector *conn = nouveau_connector(connector); + nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); + } + return 0; } diff --git a/drivers/gpu/drm/nouveau/nv10_gpio.c b/drivers/gpu/drm/nouveau/nv10_gpio.c index 419d6495649b..550ad3fcf0af 100644 --- a/drivers/gpu/drm/nouveau/nv10_gpio.c +++ b/drivers/gpu/drm/nouveau/nv10_gpio.c @@ -27,6 +27,7 @@ #include "drmP.h" #include "nouveau_drv.h" #include "nouveau_hw.h" +#include "nouveau_gpio.h" int nv10_gpio_sense(struct drm_device *dev, int line) @@ -80,3 +81,43 @@ nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out) NVWriteCRTC(dev, 0, reg, mask | (data << line)); return 0; } + +void +nv10_gpio_irq_enable(struct drm_device *dev, int line, bool on) +{ + u32 mask = 0x00010001 << line; + + nv_wr32(dev, 0x001104, mask); + nv_mask(dev, 0x001144, mask, on ? mask : 0); +} + +static void +nv10_gpio_isr(struct drm_device *dev) +{ + u32 intr = nv_rd32(dev, 0x1104); + u32 hi = (intr & 0x0000ffff) >> 0; + u32 lo = (intr & 0xffff0000) >> 16; + + nouveau_gpio_isr(dev, 0, hi | lo); + + nv_wr32(dev, 0x001104, intr); +} + +int +nv10_gpio_init(struct drm_device *dev) +{ + nv_wr32(dev, 0x001140, 0x00000000); + nv_wr32(dev, 0x001100, 0xffffffff); + nv_wr32(dev, 0x001144, 0x00000000); + nv_wr32(dev, 0x001104, 0xffffffff); + nouveau_irq_register(dev, 28, nv10_gpio_isr); /* PBUS */ + return 0; +} + +void +nv10_gpio_fini(struct drm_device *dev) +{ + nv_wr32(dev, 0x001140, 0x00000000); + nv_wr32(dev, 0x001144, 0x00000000); + nouveau_irq_unregister(dev, 28); +} -- cgit v1.2.3 From 52c4d767437b40b0cbc02d6a4480abb45ace64bb Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 24 Nov 2011 12:59:44 +1000 Subject: drm/nouveau: move hpd enable/disable to common code No idea why I didn't do this initially... NVD9 HPD is now enabled. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 20 ++++++++++++++++++-- drivers/gpu/drm/nouveau/nv04_display.c | 8 -------- drivers/gpu/drm/nouveau/nv50_display.c | 17 ----------------- 3 files changed, 18 insertions(+), 27 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index b186174fa72f..3cb52bc52b21 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -33,6 +33,7 @@ #include "nouveau_crtc.h" #include "nouveau_dma.h" #include "nouveau_connector.h" +#include "nouveau_gpio.h" #include "nv50_display.h" static void @@ -211,11 +212,19 @@ nouveau_display_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct drm_connector *connector; int ret; ret = disp->init(dev); - if (ret == 0) { - drm_kms_helper_poll_enable(dev); + if (ret) + return ret; + + drm_kms_helper_poll_enable(dev); + + /* enable hotplug interrupts */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct nouveau_connector *conn = nouveau_connector(connector); + nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); } return ret; @@ -226,6 +235,13 @@ nouveau_display_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct drm_connector *connector; + + /* disable hotplug interrupts */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct nouveau_connector *conn = nouveau_connector(connector); + nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, false); + } drm_kms_helper_poll_disable(dev); disp->fini(dev); diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 15b748f0ea4b..7047d37e8dab 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -31,7 +31,6 @@ #include "nouveau_hw.h" #include "nouveau_encoder.h" #include "nouveau_connector.h" -#include "nouveau_gpio.h" static void nv04_vblank_crtc0_isr(struct drm_device *); static void nv04_vblank_crtc1_isr(struct drm_device *); @@ -221,7 +220,6 @@ nv04_display_destroy(struct drm_device *dev) int nv04_display_init(struct drm_device *dev) { - struct drm_connector *connector; struct drm_encoder *encoder; struct drm_crtc *crtc; @@ -242,12 +240,6 @@ nv04_display_init(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) crtc->funcs->restore(crtc); - /* enable hotplug interrupts */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct nouveau_connector *conn = nouveau_connector(connector); - nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); - } - return 0; } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index f408e105a0cd..7ba28e08ee31 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -32,7 +32,6 @@ #include "nouveau_fb.h" #include "nouveau_fbcon.h" #include "nouveau_ramht.h" -#include "nouveau_gpio.h" #include "drm_crtc_helper.h" static void nv50_display_isr(struct drm_device *); @@ -141,7 +140,6 @@ nv50_display_sync(struct drm_device *dev) int nv50_display_init(struct drm_device *dev) { - struct drm_connector *connector; struct nouveau_channel *evo; int ret, i; u32 val; @@ -236,12 +234,6 @@ nv50_display_init(struct drm_device *dev) NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 | NV50_PDISPLAY_INTR_EN_1_CLK_UNK40); - /* enable hotplug interrupts */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct nouveau_connector *conn = nouveau_connector(connector); - nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); - } - ret = nv50_evo_init(dev); if (ret) return ret; @@ -262,7 +254,6 @@ nv50_display_init(struct drm_device *dev) void nv50_display_fini(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_display *disp = nv50_display(dev); struct nouveau_channel *evo = disp->master; struct drm_crtc *drm_crtc; @@ -325,14 +316,6 @@ nv50_display_fini(struct drm_device *dev) /* disable interrupts. */ nv_wr32(dev, NV50_PDISPLAY_INTR_EN_1, 0x00000000); - - /* disable hotplug interrupts */ - nv_wr32(dev, 0xe054, 0xffffffff); - nv_wr32(dev, 0xe050, 0x00000000); - if (dev_priv->chipset >= 0x90) { - nv_wr32(dev, 0xe074, 0xffffffff); - nv_wr32(dev, 0xe070, 0x00000000); - } } int -- cgit v1.2.3 From 045da4e55581d9b4de135bbdbdd1b7fa98dc18a9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 29 Oct 2011 00:22:49 +1000 Subject: drm/nvc0/pm: initial engine reclocking Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.h | 2 + drivers/gpu/drm/nouveau/nouveau_state.c | 4 + drivers/gpu/drm/nouveau/nvc0_pm.c | 237 ++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 7e0cc2eeb307..2f8e14fbcff8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -72,6 +72,8 @@ int nva3_pm_clocks_set(struct drm_device *, void *); /* nvc0_pm.c */ int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nvc0_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +int nvc0_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 57ccda47a70b..f5e98910d17f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -417,6 +417,8 @@ 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.clocks_get = nvc0_pm_clocks_get; + engine->pm.clocks_pre = nvc0_pm_clocks_pre; + engine->pm.clocks_set = nvc0_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->pm.pwm_get = nv50_pm_pwm_get; @@ -468,6 +470,8 @@ 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.clocks_get = nvc0_pm_clocks_get; + engine->pm.clocks_pre = nvc0_pm_clocks_pre; + engine->pm.clocks_set = nvc0_pm_clocks_set; 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 index 929aded35cb5..e9992f62c1c0 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -153,3 +153,240 @@ nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) perflvl->vdec = read_clk(dev, 0x0e); return 0; } + +struct nvc0_pm_clock { + u32 freq; + u32 ssel; + u32 mdiv; + u32 dsrc; + u32 ddiv; + u32 coef; +}; + +struct nvc0_pm_state { + struct nvc0_pm_clock eng[16]; +}; + +static u32 +calc_div(struct drm_device *dev, int clk, u32 ref, u32 freq, u32 *ddiv) +{ + u32 div = min((ref * 2) / freq, (u32)65); + if (div < 2) + div = 2; + + *ddiv = div - 2; + return (ref * 2) / div; +} + +static u32 +calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv) +{ + u32 sclk; + + /* use one of the fixed frequencies if possible */ + *ddiv = 0x00000000; + switch (freq) { + case 27000: + case 108000: + *dsrc = 0x00000000; + if (freq == 108000) + *dsrc |= 0x00030000; + return freq; + case 100000: + *dsrc = 0x00000002; + return freq; + default: + *dsrc = 0x00000003; + break; + } + + /* otherwise, calculate the closest divider */ + sclk = read_vco(dev, clk); + if (clk < 7) + sclk = calc_div(dev, clk, sclk, freq, ddiv); + return sclk; +} + +static u32 +calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef) +{ + struct pll_lims limits; + int N, M, P, ret; + + ret = get_pll_limits(dev, 0x137000 + (clk * 0x20), &limits); + if (ret) + return 0; + + limits.refclk = read_div(dev, clk, 0x137120, 0x137140); + if (!limits.refclk) + return 0; + + ret = nva3_calc_pll(dev, &limits, freq, &N, NULL, &M, &P); + if (ret <= 0) + return 0; + + *coef = (P << 16) | (N << 8) | M; + return ret; +} + +/* A (likely rather simplified and incomplete) view of the clock tree + * + * Key: + * + * S: source select + * D: divider + * P: pll + * F: switch + * + * Engine clocks: + * + * 137250(D) ---- 137100(F0) ---- 137160(S)/1371d0(D) ------------------- ref + * (F1) ---- 1370X0(P) ---- 137120(S)/137140(D) ---- ref + * + * Not all registers exist for all clocks. For example: clocks >= 8 don't + * have their own PLL (all tied to clock 7's PLL when in PLL mode), nor do + * they have the divider at 1371d0, though the source selection at 137160 + * still exists. You must use the divider at 137250 for these instead. + * + * Memory clock: + * + * TBD, read_mem() above is likely very wrong... + * + */ + +static int +calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq) +{ + u32 src0, div0, div1D, div1P = 0; + u32 clk0, clk1 = 0; + + /* invalid clock domain */ + if (!freq) + return 0; + + /* first possible path, using only dividers */ + clk0 = calc_src(dev, clk, freq, &src0, &div0); + clk0 = calc_div(dev, clk, clk0, freq, &div1D); + + /* see if we can get any closer using PLLs */ + if (clk0 != freq) { + if (clk < 7) + clk1 = calc_pll(dev, clk, freq, &info->coef); + else + clk1 = read_pll(dev, 0x1370e0); + clk1 = calc_div(dev, clk, clk1, freq, &div1P); + } + + /* select the method which gets closest to target freq */ + if (abs((int)freq - clk0) <= abs((int)freq - clk1)) { + info->dsrc = src0; + if (div0) { + info->ddiv |= 0x80000000; + info->ddiv |= div0 << 8; + info->ddiv |= div0; + } + if (div1D) { + info->mdiv |= 0x80000000; + info->mdiv |= div1D; + } + info->ssel = 0; + info->freq = clk0; + } else { + if (div1P) { + info->mdiv |= 0x80000000; + info->mdiv |= div1P << 8; + } + info->ssel = (1 << clk); + info->freq = clk1; + } + + return 0; +} + +void * +nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_pm_state *info; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + /* NFI why this is still in the performance table, the ROPCs appear + * to get their clock from clock 2 ("hub07", actually hub05 on this + * chip, but, anyway...) as well. nvatiming confirms hub05 and ROP + * are always the same freq with the binary driver even when the + * performance table says they should differ. + */ + if (dev_priv->chipset == 0xd9) + perflvl->rop = 0; + + if ((ret = calc_clk(dev, 0x00, &info->eng[0x00], perflvl->shader)) || + (ret = calc_clk(dev, 0x01, &info->eng[0x01], perflvl->rop)) || + (ret = calc_clk(dev, 0x02, &info->eng[0x02], perflvl->hub07)) || + (ret = calc_clk(dev, 0x07, &info->eng[0x07], perflvl->hub06)) || + (ret = calc_clk(dev, 0x08, &info->eng[0x08], perflvl->hub01)) || + (ret = calc_clk(dev, 0x09, &info->eng[0x09], perflvl->copy)) || + (ret = calc_clk(dev, 0x0c, &info->eng[0x0c], perflvl->daemon)) || + (ret = calc_clk(dev, 0x0e, &info->eng[0x0e], perflvl->vdec))) { + kfree(info); + return ERR_PTR(ret); + } + + return info; +} + +static void +prog_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info) +{ + /* program dividers at 137160/1371d0 first */ + if (clk < 7 && !info->ssel) { + nv_mask(dev, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv); + nv_wr32(dev, 0x137160 + (clk * 0x04), info->dsrc); + } + + /* switch clock to non-pll mode */ + nv_mask(dev, 0x137100, (1 << clk), 0x00000000); + nv_wait(dev, 0x137100, (1 << clk), 0x00000000); + + /* reprogram pll */ + if (clk < 7) { + /* make sure it's disabled first... */ + u32 base = 0x137000 + (clk * 0x20); + u32 ctrl = nv_rd32(dev, base + 0x00); + if (ctrl & 0x00000001) { + nv_mask(dev, base + 0x00, 0x00000004, 0x00000000); + nv_mask(dev, base + 0x00, 0x00000001, 0x00000000); + } + /* program it to new values, if necessary */ + if (info->ssel) { + nv_wr32(dev, base + 0x04, info->coef); + nv_mask(dev, base + 0x00, 0x00000001, 0x00000001); + nv_wait(dev, base + 0x00, 0x00020000, 0x00020000); + nv_mask(dev, base + 0x00, 0x00020004, 0x00000004); + } + } + + /* select pll/non-pll mode, and program final clock divider */ + nv_mask(dev, 0x137100, (1 << clk), info->ssel); + nv_wait(dev, 0x137100, (1 << clk), info->ssel); + nv_mask(dev, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv); +} + +int +nvc0_pm_clocks_set(struct drm_device *dev, void *data) +{ + struct nvc0_pm_state *info = data; + int i; + + for (i = 0; i < 16; i++) { + if (!info->eng[i].freq) + continue; + prog_clk(dev, i, &info->eng[i]); + } + + kfree(info); + return 0; +} -- cgit v1.2.3 From 4e03b4af6dd3cff445fc0455805b43b101647bfc Mon Sep 17 00:00:00 2001 From: Francisco Jerez Date: Sat, 19 Nov 2011 11:57:52 +0100 Subject: drm/nouveau: Fix pushbufs over the 4GB mark. Signed-off-by: Francisco Jerez Tested-by: Ben Skeggs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_channel.c | 2 ++ drivers/gpu/drm/nouveau/nouveau_debugfs.c | 2 +- drivers/gpu/drm/nouveau/nouveau_dma.c | 14 ++++++++------ drivers/gpu/drm/nouveau/nouveau_drv.h | 3 ++- drivers/gpu/drm/nouveau/nv50_fifo.c | 6 ++++-- 5 files changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index bb6ec9ef8676..a018defb7621 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -187,6 +187,8 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, nouveau_dma_pre_init(chan); chan->user_put = 0x40; chan->user_get = 0x44; + if (dev_priv->card_type >= NV_50) + chan->user_get_hi = 0x60; /* disable the fifo caches */ pfifo->reassign(dev, false); diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index f52c2db3529e..fa2ec491f6a7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -44,7 +44,7 @@ nouveau_debugfs_channel_info(struct seq_file *m, void *data) seq_printf(m, "channel id : %d\n", chan->id); seq_printf(m, "cpu fifo state:\n"); - seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base); + seq_printf(m, " base: 0x%10llx\n", chan->pushbuf_base); seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2); seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2); seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2); diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c index 00bc6eaad558..4c2e4e5925fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.c +++ b/drivers/gpu/drm/nouveau/nouveau_dma.c @@ -134,11 +134,13 @@ OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords) * -EBUSY if timeout exceeded */ static inline int -READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout) +READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout) { - uint32_t val; + uint64_t val; val = nvchan_rd32(chan, chan->user_get); + if (chan->user_get_hi) + val |= (uint64_t)nvchan_rd32(chan, chan->user_get_hi) << 32; /* reset counter as long as GET is still advancing, this is * to avoid misdetecting a GPU lockup if the GPU happens to @@ -218,8 +220,8 @@ nv50_dma_push_wait(struct nouveau_channel *chan, int count) static int nv50_dma_wait(struct nouveau_channel *chan, int slots, int count) { - uint32_t cnt = 0, prev_get = 0; - int ret; + uint64_t prev_get = 0; + int ret, cnt = 0; ret = nv50_dma_push_wait(chan, slots + 1); if (unlikely(ret)) @@ -261,8 +263,8 @@ nv50_dma_wait(struct nouveau_channel *chan, int slots, int count) int nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size) { - uint32_t prev_get = 0, cnt = 0; - int get; + uint64_t prev_get = 0; + int cnt = 0, get; if (chan->dma.ib_max) return nv50_dma_wait(chan, slots, size); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 0af525820347..38134a9c7578 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -232,6 +232,7 @@ struct nouveau_channel { /* mapping of the regs controlling the fifo */ void __iomem *user; uint32_t user_get; + uint32_t user_get_hi; uint32_t user_put; /* Fencing */ @@ -249,7 +250,7 @@ struct nouveau_channel { struct nouveau_gpuobj *pushbuf; struct nouveau_bo *pushbuf_bo; struct nouveau_vma pushbuf_vma; - uint32_t pushbuf_base; + uint64_t pushbuf_base; /* Notifier memory */ struct nouveau_bo *notifier_bo; diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index c34a074f7ea1..3bc2a565c20b 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -230,6 +230,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan) struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *ramfc = NULL; + uint64_t ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4; unsigned long flags; int ret; @@ -280,8 +281,9 @@ nv50_fifo_create_context(struct nouveau_channel *chan) nv_wo32(ramfc, 0x7c, 0x30000001); nv_wo32(ramfc, 0x78, 0x00000000); nv_wo32(ramfc, 0x3c, 0x403f6078); - nv_wo32(ramfc, 0x50, chan->pushbuf_base + chan->dma.ib_base * 4); - nv_wo32(ramfc, 0x54, drm_order(chan->dma.ib_max + 1) << 16); + nv_wo32(ramfc, 0x50, lower_32_bits(ib_offset)); + nv_wo32(ramfc, 0x54, upper_32_bits(ib_offset) | + drm_order(chan->dma.ib_max + 1) << 16); if (dev_priv->chipset != 0x50) { nv_wo32(chan->ramin, 0, chan->id); -- cgit v1.2.3 From b2e0d195d29326fce6aef8f2f789028bf21d743f Mon Sep 17 00:00:00 2001 From: Francisco Jerez Date: Sat, 19 Nov 2011 17:22:37 +0100 Subject: drm/nouveau: Fix notifier blocks over the 4GB mark. Signed-off-by: Francisco Jerez Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_notifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c index 6abdbe6530a7..2ef883c4bbc1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_notifier.c +++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c @@ -115,7 +115,7 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *nobj = NULL; struct drm_mm_node *mem; - uint32_t offset; + uint64_t offset; int target, ret; mem = drm_mm_search_free_in_range(&chan->notifier_heap, size, 0, -- cgit v1.2.3 From f7b24c42da1a7bbb98145d27aa716d8af3cae2a6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 22 Dec 2011 15:20:21 +1000 Subject: drm/nouveau/ttm: fix crash as a result of a recent ttm change "drm/ttm: callback move_notify any time bo placement change v4" failed to avoid a NULL pointer dereference in nouveau caused by move_notify being expected to handle that case now. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bo.c | 8 +++----- drivers/gpu/drm/nouveau/nouveau_vm.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_vm.h | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index f12dd0f39211..8cf4a48f872e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -682,8 +682,7 @@ nouveau_vma_getmap(struct nouveau_channel *chan, struct nouveau_bo *nvbo, if (mem->mem_type == TTM_PL_VRAM) nouveau_vm_map(vma, node); else - nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT, - node, node->pages); + nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT, node); return 0; } @@ -810,7 +809,6 @@ out: static void nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) { - struct nouveau_mem *node = new_mem->mm_node; struct nouveau_bo *nvbo = nouveau_bo(bo); struct nouveau_vma *vma; @@ -822,7 +820,7 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) nvbo->page_shift == vma->vm->spg_shift) { nouveau_vm_map_sg(vma, 0, new_mem-> num_pages << PAGE_SHIFT, - node, node->pages); + new_mem->mm_node); } else { nouveau_vm_unmap(vma); } @@ -1173,7 +1171,7 @@ nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm, nouveau_vm_map(vma, nvbo->bo.mem.mm_node); else if (nvbo->bo.mem.mem_type == TTM_PL_TT) - nouveau_vm_map_sg(vma, 0, size, node, node->pages); + nouveau_vm_map_sg(vma, 0, size, node); list_add_tail(&vma->head, &nvbo->vma_list); vma->refcount = 1; diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c index ef0832b29ad2..2bf6c0350b4b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vm.c @@ -78,9 +78,10 @@ nouveau_vm_map(struct nouveau_vma *vma, struct nouveau_mem *node) void nouveau_vm_map_sg(struct nouveau_vma *vma, u64 delta, u64 length, - struct nouveau_mem *mem, dma_addr_t *list) + struct nouveau_mem *mem) { struct nouveau_vm *vm = vma->vm; + dma_addr_t *list = mem->pages; int big = vma->node->type != vm->spg_shift; u32 offset = vma->node->offset + (delta >> 12); u32 bits = vma->node->type - 12; diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h index 6ce995f7797e..4fb6e728734d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.h +++ b/drivers/gpu/drm/nouveau/nouveau_vm.h @@ -89,7 +89,7 @@ void nouveau_vm_map_at(struct nouveau_vma *, u64 offset, struct nouveau_mem *); void nouveau_vm_unmap(struct nouveau_vma *); void nouveau_vm_unmap_at(struct nouveau_vma *, u64 offset, u64 length); void nouveau_vm_map_sg(struct nouveau_vma *, u64 offset, u64 length, - struct nouveau_mem *, dma_addr_t *); + struct nouveau_mem *); /* nv50_vm.c */ void nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, -- cgit v1.2.3