From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild | 44 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c | 165 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c | 115 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c | 120 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c | 122 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h | 28 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c | 37 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c | 68 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c | 37 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c | 38 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c | 87 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h | 53 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c | 70 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c | 43 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c | 94 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c | 60 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c | 140 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c | 61 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c | 61 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c | 75 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h | 14 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c | 68 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c | 77 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c | 57 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c | 44 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c | 44 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c | 43 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c | 320 ++++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h | 31 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h | 73 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h | 180 +++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c | 731 +++++++++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c | 1639 ++++++++++++++++++++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c | 55 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c | 1012 ++++++++++++ .../gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c | 101 ++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c | 79 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c | 59 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c | 72 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c | 62 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c | 212 +++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c | 66 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c | 64 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c | 66 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c | 54 + .../gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c | 465 ++++++ .../gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h | 15 + .../gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h | 22 + .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c | 93 ++ .../drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c | 119 ++ 53 files changed, 7569 insertions(+) create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c create mode 100644 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c (limited to 'kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb') diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild new file mode 100644 index 000000000..d6be4c6c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild @@ -0,0 +1,44 @@ +nvkm-y += nvkm/subdev/fb/base.o +nvkm-y += nvkm/subdev/fb/nv04.o +nvkm-y += nvkm/subdev/fb/nv10.o +nvkm-y += nvkm/subdev/fb/nv1a.o +nvkm-y += nvkm/subdev/fb/nv20.o +nvkm-y += nvkm/subdev/fb/nv25.o +nvkm-y += nvkm/subdev/fb/nv30.o +nvkm-y += nvkm/subdev/fb/nv35.o +nvkm-y += nvkm/subdev/fb/nv36.o +nvkm-y += nvkm/subdev/fb/nv40.o +nvkm-y += nvkm/subdev/fb/nv41.o +nvkm-y += nvkm/subdev/fb/nv44.o +nvkm-y += nvkm/subdev/fb/nv46.o +nvkm-y += nvkm/subdev/fb/nv47.o +nvkm-y += nvkm/subdev/fb/nv49.o +nvkm-y += nvkm/subdev/fb/nv4e.o +nvkm-y += nvkm/subdev/fb/nv50.o +nvkm-y += nvkm/subdev/fb/g84.o +nvkm-y += nvkm/subdev/fb/gt215.o +nvkm-y += nvkm/subdev/fb/mcp77.o +nvkm-y += nvkm/subdev/fb/mcp89.o +nvkm-y += nvkm/subdev/fb/gf100.o +nvkm-y += nvkm/subdev/fb/gk104.o +nvkm-y += nvkm/subdev/fb/gk20a.o +nvkm-y += nvkm/subdev/fb/gm107.o +nvkm-y += nvkm/subdev/fb/ramnv04.o +nvkm-y += nvkm/subdev/fb/ramnv10.o +nvkm-y += nvkm/subdev/fb/ramnv1a.o +nvkm-y += nvkm/subdev/fb/ramnv20.o +nvkm-y += nvkm/subdev/fb/ramnv40.o +nvkm-y += nvkm/subdev/fb/ramnv41.o +nvkm-y += nvkm/subdev/fb/ramnv44.o +nvkm-y += nvkm/subdev/fb/ramnv49.o +nvkm-y += nvkm/subdev/fb/ramnv4e.o +nvkm-y += nvkm/subdev/fb/ramnv50.o +nvkm-y += nvkm/subdev/fb/ramgt215.o +nvkm-y += nvkm/subdev/fb/rammcp77.o +nvkm-y += nvkm/subdev/fb/ramgf100.o +nvkm-y += nvkm/subdev/fb/ramgk104.o +nvkm-y += nvkm/subdev/fb/ramgm107.o +nvkm-y += nvkm/subdev/fb/sddr2.o +nvkm-y += nvkm/subdev/fb/sddr3.o +nvkm-y += nvkm/subdev/fb/gddr3.o +nvkm-y += nvkm/subdev/fb/gddr5.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c new file mode 100644 index 000000000..61fde43da --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c @@ -0,0 +1,165 @@ +/* + * Copyright 2012 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 "priv.h" + +#include +#include + +int +nvkm_fb_bios_memtype(struct nvkm_bios *bios) +{ + const u8 ramcfg = (nv_rd32(bios, 0x101000) & 0x0000003c) >> 2; + struct nvbios_M0203E M0203E; + u8 ver, hdr; + + if (nvbios_M0203Em(bios, ramcfg, &ver, &hdr, &M0203E)) { + switch (M0203E.type) { + case M0203E_TYPE_DDR2 : return NV_MEM_TYPE_DDR2; + case M0203E_TYPE_DDR3 : return NV_MEM_TYPE_DDR3; + case M0203E_TYPE_GDDR3: return NV_MEM_TYPE_GDDR3; + case M0203E_TYPE_GDDR5: return NV_MEM_TYPE_GDDR5; + default: + nv_warn(bios, "M0203E type %02x\n", M0203E.type); + return NV_MEM_TYPE_UNKNOWN; + } + } + + nv_warn(bios, "M0203E not matched!\n"); + return NV_MEM_TYPE_UNKNOWN; +} + +int +_nvkm_fb_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_fb *pfb = (void *)object; + int ret; + + if (pfb->ram) { + ret = nv_ofuncs(pfb->ram)->fini(nv_object(pfb->ram), suspend); + if (ret && suspend) + return ret; + } + + return nvkm_subdev_fini(&pfb->base, suspend); +} + +int +_nvkm_fb_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object; + int ret, i; + + ret = nvkm_subdev_init(&pfb->base); + if (ret) + return ret; + + if (pfb->ram) { + ret = nv_ofuncs(pfb->ram)->init(nv_object(pfb->ram)); + if (ret) + return ret; + } + + for (i = 0; i < pfb->tile.regions; i++) + pfb->tile.prog(pfb, i, &pfb->tile.region[i]); + + return 0; +} + +void +_nvkm_fb_dtor(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object; + int i; + + for (i = 0; i < pfb->tile.regions; i++) + pfb->tile.fini(pfb, i, &pfb->tile.region[i]); + nvkm_mm_fini(&pfb->tags); + + if (pfb->ram) { + nvkm_mm_fini(&pfb->vram); + nvkm_object_ref(NULL, (struct nvkm_object **)&pfb->ram); + } + + nvkm_subdev_destroy(&pfb->base); +} + +int +nvkm_fb_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + struct nvkm_fb_impl *impl = (void *)oclass; + static const char *name[] = { + [NV_MEM_TYPE_UNKNOWN] = "unknown", + [NV_MEM_TYPE_STOLEN ] = "stolen system memory", + [NV_MEM_TYPE_SGRAM ] = "SGRAM", + [NV_MEM_TYPE_SDRAM ] = "SDRAM", + [NV_MEM_TYPE_DDR1 ] = "DDR1", + [NV_MEM_TYPE_DDR2 ] = "DDR2", + [NV_MEM_TYPE_DDR3 ] = "DDR3", + [NV_MEM_TYPE_GDDR2 ] = "GDDR2", + [NV_MEM_TYPE_GDDR3 ] = "GDDR3", + [NV_MEM_TYPE_GDDR4 ] = "GDDR4", + [NV_MEM_TYPE_GDDR5 ] = "GDDR5", + }; + struct nvkm_object *ram; + struct nvkm_fb *pfb; + int ret; + + ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PFB", "fb", + length, pobject); + pfb = *pobject; + if (ret) + return ret; + + pfb->memtype_valid = impl->memtype; + + if (!impl->ram) + return 0; + + ret = nvkm_object_ctor(nv_object(pfb), NULL, impl->ram, NULL, 0, &ram); + if (ret) { + nv_fatal(pfb, "error detecting memory configuration!!\n"); + return ret; + } + + pfb->ram = (void *)ram; + + if (!nvkm_mm_initialised(&pfb->vram)) { + ret = nvkm_mm_init(&pfb->vram, 0, pfb->ram->size >> 12, 1); + if (ret) + return ret; + } + + if (!nvkm_mm_initialised(&pfb->tags)) { + ret = nvkm_mm_init(&pfb->tags, 0, pfb->ram->tags ? + ++pfb->ram->tags : 0, 1); + if (ret) + return ret; + } + + nv_info(pfb, "RAM type: %s\n", name[pfb->ram->type]); + nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram->size >> 20)); + nv_info(pfb, " ZCOMP: %d tags\n", pfb->ram->tags); + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c new file mode 100644 index 000000000..6c968d1e9 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 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 "nv50.h" + +struct nvkm_oclass * +g84_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x84), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &nv50_ram_oclass, + .trap = 0x001d07ff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c new file mode 100644 index 000000000..15b462ae3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c @@ -0,0 +1,115 @@ +/* + * Copyright 2013 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 + * Roy Spliet + */ +#include "priv.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramgddr3_cl_lo[] = { + { 7, 7 }, { 8, 0 }, { 9, 1 }, { 10, 2 }, { 11, 3 }, + /* the below are mentioned in some, but not all, gddr3 docs */ + { 12, 4 }, { 13, 5 }, { 14, 6 }, + /* XXX: Per Samsung docs, are these used? They overlap with Qimonda */ + /* { 4, 4 }, { 5, 5 }, { 6, 6 }, { 12, 8 }, { 13, 9 }, { 14, 10 }, + * { 15, 11 }, */ + { -1 } +}; + +static const struct ramxlat +ramgddr3_cl_hi[] = { + { 10, 2 }, { 11, 3 }, { 12, 4 }, { 13, 5 }, { 14, 6 }, { 15, 7 }, + { 16, 0 }, { 17, 1 }, + { -1 } +}; + +static const struct ramxlat +ramgddr3_wr_lo[] = { + { 5, 2 }, { 7, 4 }, { 8, 5 }, { 9, 6 }, { 10, 7 }, + { 11, 0 }, + /* the below are mentioned in some, but not all, gddr3 docs */ + { 4, 1 }, { 6, 3 }, { 12, 1 }, { 13 , 2 }, + { -1 } +}; + +int +nvkm_gddr3_calc(struct nvkm_ram *ram) +{ + int CL, WR, CWL, DLL = 0, ODT = 0, hi; + + switch (ram->next->bios.timing_ver) { + case 0x10: + CWL = ram->next->bios.timing_10_CWL; + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_DLLoff; + ODT = ram->next->bios.timing_10_ODT; + break; + case 0x20: + CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0; + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + /* XXX: Get these values from the VBIOS instead */ + DLL = !(ram->mr[1] & 0x1); + ODT = (ram->mr[1] & 0x004) >> 2 | + (ram->mr[1] & 0x040) >> 5 | + (ram->mr[1] & 0x200) >> 7; + break; + default: + return -ENOSYS; + } + + hi = ram->mr[2] & 0x1; + CL = ramxlat(hi ? ramgddr3_cl_hi : ramgddr3_cl_lo, CL); + WR = ramxlat(ramgddr3_wr_lo, WR); + if (CL < 0 || CWL < 1 || CWL > 7 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf74; + ram->mr[0] |= (CWL & 0x07) << 9; + ram->mr[0] |= (CL & 0x07) << 4; + ram->mr[0] |= (CL & 0x08) >> 1; + + ram->mr[1] &= ~0x3fc; + ram->mr[1] |= (ODT & 0x03) << 2; + ram->mr[1] |= (ODT & 0x03) << 8; + ram->mr[1] |= (WR & 0x03) << 4; + ram->mr[1] |= (WR & 0x04) << 5; + ram->mr[1] |= !DLL << 6; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c new file mode 100644 index 000000000..f6f9eee1d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c @@ -0,0 +1,120 @@ +/* + * Copyright 2013 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 "priv.h" + +/* binary driver only executes this path if the condition (a) is true + * for any configuration (combination of rammap+ramcfg+timing) that + * can be reached on a given card. for now, we will execute the branch + * unconditionally in the hope that a "false everywhere" in the bios + * tables doesn't actually mean "don't touch this". + */ +#define NOTE00(a) 1 + +int +nvkm_gddr5_calc(struct nvkm_ram *ram, bool nuts) +{ + int pd, lf, xd, vh, vr, vo, l3; + int WL, CL, WR, at[2], dt, ds; + int rq = ram->freq < 1000000; /* XXX */ + + switch (ram->next->bios.ramcfg_ver) { + case 0x11: + pd = ram->next->bios.ramcfg_11_01_80; + lf = ram->next->bios.ramcfg_11_01_40; + xd = !ram->next->bios.ramcfg_11_01_20; + vh = ram->next->bios.ramcfg_11_02_10; + vr = ram->next->bios.ramcfg_11_02_04; + vo = ram->next->bios.ramcfg_11_06; + l3 = !ram->next->bios.ramcfg_11_07_02; + break; + default: + return -ENOSYS; + } + + switch (ram->next->bios.timing_ver) { + case 0x20: + WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f); + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + at[0] = ram->next->bios.timing_20_2e_c0; + at[1] = ram->next->bios.timing_20_2e_30; + dt = ram->next->bios.timing_20_2e_03; + ds = ram->next->bios.timing_20_2f_03; + break; + default: + return -ENOSYS; + } + + if (WL < 1 || WL > 7 || CL < 5 || CL > 36 || WR < 4 || WR > 35) + return -EINVAL; + CL -= 5; + WR -= 4; + + ram->mr[0] &= ~0xf7f; + ram->mr[0] |= (WR & 0x0f) << 8; + ram->mr[0] |= (CL & 0x0f) << 3; + ram->mr[0] |= (WL & 0x07) << 0; + + ram->mr[1] &= ~0x0bf; + ram->mr[1] |= (xd & 0x01) << 7; + ram->mr[1] |= (at[0] & 0x03) << 4; + ram->mr[1] |= (dt & 0x03) << 2; + ram->mr[1] |= (ds & 0x03) << 0; + + /* this seems wrong, alternate field used for the broadcast + * on nuts vs non-nuts configs.. meh, it matches for now. + */ + ram->mr1_nuts = ram->mr[1]; + if (nuts) { + ram->mr[1] &= ~0x030; + ram->mr[1] |= (at[1] & 0x03) << 4; + } + + ram->mr[3] &= ~0x020; + ram->mr[3] |= (rq & 0x01) << 5; + + ram->mr[5] &= ~0x004; + ram->mr[5] |= (l3 << 2); + + if (!vo) + vo = (ram->mr[6] & 0xff0) >> 4; + if (ram->mr[6] & 0x001) + pd = 1; /* binary driver does this.. bug? */ + ram->mr[6] &= ~0xff1; + ram->mr[6] |= (vo & 0xff) << 4; + ram->mr[6] |= (pd & 0x01) << 0; + + if (NOTE00(vr)) { + ram->mr[7] &= ~0x300; + ram->mr[7] |= (vr & 0x03) << 8; + } + ram->mr[7] &= ~0x088; + ram->mr[7] |= (vh & 0x01) << 7; + ram->mr[7] |= (lf & 0x01) << 3; + + ram->mr[8] &= ~0x003; + ram->mr[8] |= (WR & 0x10) >> 3; + ram->mr[8] |= (CL & 0x10) >> 4; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c new file mode 100644 index 000000000..d51aa0237 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c @@ -0,0 +1,122 @@ +/* + * Copyright 2012 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 "gf100.h" + +#include + +extern const u8 gf100_pte_storage_type_map[256]; + +bool +gf100_fb_memtype_valid(struct nvkm_fb *pfb, u32 tile_flags) +{ + u8 memtype = (tile_flags & 0x0000ff00) >> 8; + return likely((gf100_pte_storage_type_map[memtype] != 0xff)); +} + +static void +gf100_fb_intr(struct nvkm_subdev *subdev) +{ + struct gf100_fb_priv *priv = (void *)subdev; + u32 intr = nv_rd32(priv, 0x000100); + if (intr & 0x08000000) { + nv_debug(priv, "PFFB intr\n"); + intr &= ~0x08000000; + } + if (intr & 0x00002000) { + nv_debug(priv, "PBFB intr\n"); + intr &= ~0x00002000; + } +} + +int +gf100_fb_init(struct nvkm_object *object) +{ + struct gf100_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + if (priv->r100c10_page) + nv_wr32(priv, 0x100c10, priv->r100c10 >> 8); + + nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */ + return 0; +} + +void +gf100_fb_dtor(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct gf100_fb_priv *priv = (void *)object; + + if (priv->r100c10_page) { + dma_unmap_page(nv_device_base(device), priv->r100c10, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(priv->r100c10_page); + } + + nvkm_fb_destroy(&priv->base); +} + +int +gf100_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct gf100_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (priv->r100c10_page) { + priv->r100c10 = dma_map_page(nv_device_base(device), + priv->r100c10_page, 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(nv_device_base(device), priv->r100c10)) + return -EFAULT; + } + + nv_subdev(priv)->intr = gf100_fb_intr; + return 0; +} + +struct nvkm_oclass * +gf100_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xc0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fb_ctor, + .dtor = gf100_fb_dtor, + .init = gf100_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, + .ram = &gf100_ram_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h new file mode 100644 index 000000000..0af4da259 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h @@ -0,0 +1,28 @@ +#ifndef __NVKM_RAM_NVC0_H__ +#define __NVKM_RAM_NVC0_H__ +#include "priv.h" +#include "nv50.h" + +struct gf100_fb_priv { + struct nvkm_fb base; + struct page *r100c10_page; + dma_addr_t r100c10; +}; + +int gf100_fb_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void gf100_fb_dtor(struct nvkm_object *); +int gf100_fb_init(struct nvkm_object *); +bool gf100_fb_memtype_valid(struct nvkm_fb *, u32); + +#define gf100_ram_create(p,e,o,m,d) \ + gf100_ram_create_((p), (e), (o), (m), sizeof(**d), (void **)d) +int gf100_ram_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, u32, int, void **); +int gf100_ram_get(struct nvkm_fb *, u64, u32, u32, u32, + struct nvkm_mem **); +void gf100_ram_put(struct nvkm_fb *, struct nvkm_mem **); + +int gk104_ram_init(struct nvkm_object*); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c new file mode 100644 index 000000000..1c0831766 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 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 "gf100.h" + +struct nvkm_oclass * +gk104_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xe0), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fb_ctor, + .dtor = gf100_fb_dtor, + .init = gf100_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, + .ram = &gk104_ram_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c new file mode 100644 index 000000000..a5d7857d3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. + */ +#include "gf100.h" + +struct gk20a_fb_priv { + struct nvkm_fb base; +}; + +static int +gk20a_fb_init(struct nvkm_object *object) +{ + struct gk20a_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */ + return 0; +} + +static int +gk20a_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gk20a_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass * +gk20a_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xea), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk20a_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = gk20a_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c new file mode 100644 index 000000000..843f9356b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 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 "gf100.h" + +struct nvkm_oclass * +gm107_fb_oclass = &(struct nvkm_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0x07), + .base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_fb_ctor, + .dtor = gf100_fb_dtor, + .init = gf100_fb_init, + .fini = _nvkm_fb_fini, + }, + .memtype = gf100_fb_memtype_valid, + .ram = &gm107_ram_oclass, +}.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c new file mode 100644 index 000000000..dd9b8a0a3 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 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 "nv50.h" + +struct nvkm_oclass * +gt215_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0xa3), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = >215_ram_oclass, + .trap = 0x000d0fff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c new file mode 100644 index 000000000..7be4a47ef --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 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 "nv50.h" + +struct nvkm_oclass * +mcp77_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0xaa), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &mcp77_ram_oclass, + .trap = 0x001d07ff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c new file mode 100644 index 000000000..2d00656fa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 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 "nv50.h" + +struct nvkm_oclass * +mcp89_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0xaf), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &mcp77_ram_oclass, + .trap = 0x089d1fff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c new file mode 100644 index 000000000..c063dec7d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c @@ -0,0 +1,87 @@ +/* + * Copyright 2012 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 "nv04.h" +#include "regsnv04.h" + +bool +nv04_fb_memtype_valid(struct nvkm_fb *pfb, u32 tile_flags) +{ + if (!(tile_flags & 0xff00)) + return true; + + return false; +} + +static int +nv04_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + /* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows + * nvidia reading PFB_CFG_0, then writing back its original value. + * (which was 0x701114 in this case) + */ + nv_wr32(priv, NV04_PFB_CFG0, 0x1114); + return 0; +} + +int +nv04_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nv04_fb_impl *impl = (void *)oclass; + struct nv04_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.tile.regions = impl->tile.regions; + priv->base.tile.init = impl->tile.init; + priv->base.tile.comp = impl->tile.comp; + priv->base.tile.fini = impl->tile.fini; + priv->base.tile.prog = impl->tile.prog; + return 0; +} + +struct nvkm_oclass * +nv04_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x04), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv04_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv04_ram_oclass, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h new file mode 100644 index 000000000..caa0d03aa --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h @@ -0,0 +1,53 @@ +#ifndef __NVKM_FB_NV04_H__ +#define __NVKM_FB_NV04_H__ +#include "priv.h" + +struct nv04_fb_priv { + struct nvkm_fb base; +}; + +int nv04_fb_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + +struct nv04_fb_impl { + struct nvkm_fb_impl base; + struct { + int regions; + void (*init)(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); + void (*comp)(struct nvkm_fb *, int i, u32 size, u32 flags, + struct nvkm_fb_tile *); + void (*fini)(struct nvkm_fb *, int i, + struct nvkm_fb_tile *); + void (*prog)(struct nvkm_fb *, int i, + struct nvkm_fb_tile *); + } tile; +}; + +void nv10_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +void nv10_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *); +void nv10_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +void nv20_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +void nv20_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *); +void nv20_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +int nv30_fb_init(struct nvkm_object *); +void nv30_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); + +void nv40_fb_tile_comp(struct nvkm_fb *, int i, u32 size, u32 flags, + struct nvkm_fb_tile *); + +int nv41_fb_init(struct nvkm_object *); +void nv41_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +int nv44_fb_init(struct nvkm_object *); +void nv44_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +void nv46_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c new file mode 100644 index 000000000..f3530e4a6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +void +nv10_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x80000000 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv10_fb_tile_fini(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + tile->addr = 0; + tile->limit = 0; + tile->pitch = 0; + tile->zcomp = 0; +} + +void +nv10_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100240 + (i * 0x10)); +} + +struct nvkm_oclass * +nv10_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x10), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv10_ram_oclass, + .tile.regions = 8, + .tile.init = nv10_fb_tile_init, + .tile.fini = nv10_fb_tile_fini, + .tile.prog = nv10_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c new file mode 100644 index 000000000..83bcb73ca --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv1a_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x1a), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv1a_ram_oclass, + .tile.regions = 8, + .tile.init = nv10_fb_tile_init, + .tile.fini = nv10_fb_tile_fini, + .tile.prog = nv10_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c new file mode 100644 index 000000000..e37084b8d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +void +nv20_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x00000001 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; + if (flags & 4) { + pfb->tile.comp(pfb, i, size, flags, tile); + tile->addr |= 2; + } +} + +static void +nv20_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */ + else tile->zcomp = 0x04000000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; + tile->zcomp |= 0x80000000; /* enable */ +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x08000000; +#endif + } +} + +void +nv20_fb_tile_fini(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + tile->addr = 0; + tile->limit = 0; + tile->pitch = 0; + tile->zcomp = 0; + nvkm_mm_free(&pfb->tags, &tile->tag); +} + +void +nv20_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100240 + (i * 0x10)); + nv_wr32(pfb, 0x100300 + (i * 0x04), tile->zcomp); +} + +struct nvkm_oclass * +nv20_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x20), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv20_fb_tile_init, + .tile.comp = nv20_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c new file mode 100644 index 000000000..bc9f54f38 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +static void +nv25_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */ + else tile->zcomp = 0x00200000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x01000000; +#endif + } +} + +struct nvkm_oclass * +nv25_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x25), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = _nvkm_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv20_fb_tile_init, + .tile.comp = nv25_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c new file mode 100644 index 000000000..09ebb9477 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +#include + +void +nv30_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) { + tile->addr = (0 << 4); + } else { + if (pfb->tile.comp) /* z compression */ + pfb->tile.comp(pfb, i, size, flags, tile); + tile->addr = (1 << 4); + } + + tile->addr |= 0x00000001; /* enable */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +static void +nv30_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */ + else tile->zcomp |= 0x02000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 12; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x10000000; +#endif + } +} + +static int +calc_bias(struct nv04_fb_priv *priv, int k, int i, int j) +{ + struct nvkm_device *device = nv_device(priv); + int b = (device->chipset > 0x30 ? + nv_rd32(priv, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) : + 0) & 0xf; + + return 2 * (b & 0x8 ? b - 0x10 : b); +} + +static int +calc_ref(struct nv04_fb_priv *priv, int l, int k, int i) +{ + int j, x = 0; + + for (j = 0; j < 4; j++) { + int m = (l >> (8 * i) & 0xff) + calc_bias(priv, k, i, j); + + x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j); + } + + return x; +} + +int +nv30_fb_init(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nv04_fb_priv *priv = (void *)object; + int ret, i, j; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + /* Init the memory timing regs at 0x10037c/0x1003ac */ + if (device->chipset == 0x30 || + device->chipset == 0x31 || + device->chipset == 0x35) { + /* Related to ROP count */ + int n = (device->chipset == 0x31 ? 2 : 4); + int l = nv_rd32(priv, 0x1003d0); + + for (i = 0; i < n; i++) { + for (j = 0; j < 3; j++) + nv_wr32(priv, 0x10037c + 0xc * i + 0x4 * j, + calc_ref(priv, l, 0, j)); + + for (j = 0; j < 2; j++) + nv_wr32(priv, 0x1003ac + 0x8 * i + 0x4 * j, + calc_ref(priv, l, 1, j)); + } + } + + return 0; +} + +struct nvkm_oclass * +nv30_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x30), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv30_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv30_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c new file mode 100644 index 000000000..c01dc1839 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +static void +nv35_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */ + else tile->zcomp |= 0x08000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } +} + +struct nvkm_oclass * +nv35_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x35), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv30_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv35_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c new file mode 100644 index 000000000..cad75a1ce --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +static void +nv36_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); + if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */ + else tile->zcomp |= 0x20000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 14; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x80000000; +#endif + } +} + +struct nvkm_oclass * +nv36_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x36), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv30_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv20_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv36_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c new file mode 100644 index 000000000..dbe5c1910 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +void +nv40_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x80); + u32 tags = round_up(tiles / pfb->ram->parts, 0x100); + if ( (flags & 2) && + !nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { + tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */ + tile->zcomp |= ((tile->tag->offset ) >> 8); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } +} + +static int +nv40_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_mask(priv, 0x10033c, 0x00008000, 0x00000000); + return 0; +} + +struct nvkm_oclass * +nv40_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x40), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv40_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv40_ram_oclass, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h new file mode 100644 index 000000000..602182661 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h @@ -0,0 +1,14 @@ +#ifndef __NVKM_FB_NV40_H__ +#define __NVKM_FB_NV40_H__ +#include "priv.h" + +struct nv40_ram { + struct nvkm_ram base; + u32 ctrl; + u32 coef; +}; + +int nv40_ram_calc(struct nvkm_fb *, u32); +int nv40_ram_prog(struct nvkm_fb *); +void nv40_ram_tidy(struct nvkm_fb *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c new file mode 100644 index 000000000..d9e1a40a2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +void +nv41_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100600 + (i * 0x10)); + nv_wr32(pfb, 0x100700 + (i * 0x04), tile->zcomp); +} + +int +nv41_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x100800, 0x00000001); + return 0; +} + +struct nvkm_oclass * +nv41_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x41), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv41_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv41_ram_oclass, + .tile.regions = 12, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c new file mode 100644 index 000000000..20b97c83c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +static void +nv44_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv44_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile) +{ + nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100600 + (i * 0x10)); +} + +int +nv44_fb_init(struct nvkm_object *object) +{ + struct nv04_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x100850, 0x80000000); + nv_wr32(priv, 0x100800, 0x00000001); + return 0; +} + +struct nvkm_oclass * +nv44_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x44), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv44_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv44_ram_oclass, + .tile.regions = 12, + .tile.init = nv44_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c new file mode 100644 index 000000000..5bfac38cd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +void +nv46_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) tile->addr = (0 << 3); + else tile->addr = (1 << 3); + + tile->addr |= 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +struct nvkm_oclass * +nv46_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x46), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv44_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv44_ram_oclass, + .tile.regions = 15, + .tile.init = nv46_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c new file mode 100644 index 000000000..d3b3988d1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv47_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x47), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv41_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv41_ram_oclass, + .tile.regions = 15, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c new file mode 100644 index 000000000..236e36c50 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv49_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x49), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv41_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv49_ram_oclass, + .tile.regions = 15, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c new file mode 100644 index 000000000..1352b6a73 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * 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 (including the + * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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. + * + */ +#include "nv04.h" + +struct nvkm_oclass * +nv4e_fb_oclass = &(struct nv04_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x4e), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_fb_ctor, + .dtor = _nvkm_fb_dtor, + .init = nv44_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv04_fb_memtype_valid, + .base.ram = &nv4e_ram_oclass, + .tile.regions = 12, + .tile.init = nv46_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c new file mode 100644 index 000000000..0480ce52a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c @@ -0,0 +1,320 @@ +/* + * Copyright 2012 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 "nv50.h" + +#include +#include +#include +#include + +int +nv50_fb_memtype[0x80] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 2, 1, 1, 0, 0 +}; + +bool +nv50_fb_memtype_valid(struct nvkm_fb *pfb, u32 memtype) +{ + return nv50_fb_memtype[(memtype & 0xff00) >> 8] != 0; +} + +static const struct nvkm_enum vm_dispatch_subclients[] = { + { 0x00000000, "GRCTX", NULL }, + { 0x00000001, "NOTIFY", NULL }, + { 0x00000002, "QUERY", NULL }, + { 0x00000003, "COND", NULL }, + { 0x00000004, "M2M_IN", NULL }, + { 0x00000005, "M2M_OUT", NULL }, + { 0x00000006, "M2M_NOTIFY", NULL }, + {} +}; + +static const struct nvkm_enum vm_ccache_subclients[] = { + { 0x00000000, "CB", NULL }, + { 0x00000001, "TIC", NULL }, + { 0x00000002, "TSC", NULL }, + {} +}; + +static const struct nvkm_enum vm_prop_subclients[] = { + { 0x00000000, "RT0", NULL }, + { 0x00000001, "RT1", NULL }, + { 0x00000002, "RT2", NULL }, + { 0x00000003, "RT3", NULL }, + { 0x00000004, "RT4", NULL }, + { 0x00000005, "RT5", NULL }, + { 0x00000006, "RT6", NULL }, + { 0x00000007, "RT7", NULL }, + { 0x00000008, "ZETA", NULL }, + { 0x00000009, "LOCAL", NULL }, + { 0x0000000a, "GLOBAL", NULL }, + { 0x0000000b, "STACK", NULL }, + { 0x0000000c, "DST2D", NULL }, + {} +}; + +static const struct nvkm_enum vm_pfifo_subclients[] = { + { 0x00000000, "PUSHBUF", NULL }, + { 0x00000001, "SEMAPHORE", NULL }, + {} +}; + +static const struct nvkm_enum vm_bar_subclients[] = { + { 0x00000000, "FB", NULL }, + { 0x00000001, "IN", NULL }, + {} +}; + +static const struct nvkm_enum vm_client[] = { + { 0x00000000, "STRMOUT", NULL }, + { 0x00000003, "DISPATCH", vm_dispatch_subclients }, + { 0x00000004, "PFIFO_WRITE", NULL }, + { 0x00000005, "CCACHE", vm_ccache_subclients }, + { 0x00000006, "PMSPPP", NULL }, + { 0x00000007, "CLIPID", NULL }, + { 0x00000008, "PFIFO_READ", NULL }, + { 0x00000009, "VFETCH", NULL }, + { 0x0000000a, "TEXTURE", NULL }, + { 0x0000000b, "PROP", vm_prop_subclients }, + { 0x0000000c, "PVP", NULL }, + { 0x0000000d, "PBSP", NULL }, + { 0x0000000e, "PCRYPT", NULL }, + { 0x0000000f, "PCOUNTER", NULL }, + { 0x00000011, "PDAEMON", NULL }, + {} +}; + +static const struct nvkm_enum vm_engine[] = { + { 0x00000000, "PGRAPH", NULL, NVDEV_ENGINE_GR }, + { 0x00000001, "PVP", NULL, NVDEV_ENGINE_VP }, + { 0x00000004, "PEEPHOLE", NULL }, + { 0x00000005, "PFIFO", vm_pfifo_subclients, NVDEV_ENGINE_FIFO }, + { 0x00000006, "BAR", vm_bar_subclients }, + { 0x00000008, "PMSPPP", NULL, NVDEV_ENGINE_MSPPP }, + { 0x00000008, "PMPEG", NULL, NVDEV_ENGINE_MPEG }, + { 0x00000009, "PBSP", NULL, NVDEV_ENGINE_BSP }, + { 0x0000000a, "PCRYPT", NULL, NVDEV_ENGINE_CIPHER }, + { 0x0000000b, "PCOUNTER", NULL }, + { 0x0000000c, "SEMAPHORE_BG", NULL }, + { 0x0000000d, "PCE0", NULL, NVDEV_ENGINE_CE0 }, + { 0x0000000e, "PDAEMON", NULL }, + {} +}; + +static const struct nvkm_enum vm_fault[] = { + { 0x00000000, "PT_NOT_PRESENT", NULL }, + { 0x00000001, "PT_TOO_SHORT", NULL }, + { 0x00000002, "PAGE_NOT_PRESENT", NULL }, + { 0x00000003, "PAGE_SYSTEM_ONLY", NULL }, + { 0x00000004, "PAGE_READ_ONLY", NULL }, + { 0x00000006, "NULL_DMAOBJ", NULL }, + { 0x00000007, "WRONG_MEMTYPE", NULL }, + { 0x0000000b, "VRAM_LIMIT", NULL }, + { 0x0000000f, "DMAOBJ_LIMIT", NULL }, + {} +}; + +static void +nv50_fb_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_device *device = nv_device(subdev); + struct nvkm_engine *engine; + struct nv50_fb_priv *priv = (void *)subdev; + const struct nvkm_enum *en, *cl; + struct nvkm_object *engctx = NULL; + u32 trap[6], idx, chan; + u8 st0, st1, st2, st3; + int i; + + idx = nv_rd32(priv, 0x100c90); + if (!(idx & 0x80000000)) + return; + idx &= 0x00ffffff; + + for (i = 0; i < 6; i++) { + nv_wr32(priv, 0x100c90, idx | i << 24); + trap[i] = nv_rd32(priv, 0x100c94); + } + nv_wr32(priv, 0x100c90, idx | 0x80000000); + + /* decode status bits into something more useful */ + if (device->chipset < 0xa3 || + device->chipset == 0xaa || device->chipset == 0xac) { + st0 = (trap[0] & 0x0000000f) >> 0; + st1 = (trap[0] & 0x000000f0) >> 4; + st2 = (trap[0] & 0x00000f00) >> 8; + st3 = (trap[0] & 0x0000f000) >> 12; + } else { + st0 = (trap[0] & 0x000000ff) >> 0; + st1 = (trap[0] & 0x0000ff00) >> 8; + st2 = (trap[0] & 0x00ff0000) >> 16; + st3 = (trap[0] & 0xff000000) >> 24; + } + chan = (trap[2] << 16) | trap[1]; + + en = nvkm_enum_find(vm_engine, st0); + + if (en && en->data2) { + const struct nvkm_enum *orig_en = en; + while (en->name && en->value == st0 && en->data2) { + engine = nvkm_engine(subdev, en->data2); + /*XXX: clean this up */ + if (!engine && en->data2 == NVDEV_ENGINE_BSP) + engine = nvkm_engine(subdev, NVDEV_ENGINE_MSVLD); + if (!engine && en->data2 == NVDEV_ENGINE_CIPHER) + engine = nvkm_engine(subdev, NVDEV_ENGINE_SEC); + if (!engine && en->data2 == NVDEV_ENGINE_VP) + engine = nvkm_engine(subdev, NVDEV_ENGINE_MSPDEC); + if (engine) { + engctx = nvkm_engctx_get(engine, chan); + if (engctx) + break; + } + en++; + } + if (!engctx) + en = orig_en; + } + + nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x [%s] ", + (trap[5] & 0x00000100) ? "read" : "write", + trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan, + nvkm_client_name(engctx)); + + nvkm_engctx_put(engctx); + + if (en) + pr_cont("%s/", en->name); + else + pr_cont("%02x/", st0); + + cl = nvkm_enum_find(vm_client, st2); + if (cl) + pr_cont("%s/", cl->name); + else + pr_cont("%02x/", st2); + + if (cl && cl->data) cl = nvkm_enum_find(cl->data, st3); + else if (en && en->data) cl = nvkm_enum_find(en->data, st3); + else cl = NULL; + if (cl) + pr_cont("%s", cl->name); + else + pr_cont("%02x", st3); + + pr_cont(" reason: "); + en = nvkm_enum_find(vm_fault, st1); + if (en) + pr_cont("%s\n", en->name); + else + pr_cont("0x%08x\n", st1); +} + +int +nv50_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_device *device = nv_device(parent); + struct nv50_fb_priv *priv; + int ret; + + ret = nvkm_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (priv->r100c08_page) { + priv->r100c08 = dma_map_page(nv_device_base(device), + priv->r100c08_page, 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(nv_device_base(device), priv->r100c08)) + return -EFAULT; + } else { + nv_warn(priv, "failed 0x100c08 page alloc\n"); + } + + nv_subdev(priv)->intr = nv50_fb_intr; + return 0; +} + +void +nv50_fb_dtor(struct nvkm_object *object) +{ + struct nvkm_device *device = nv_device(object); + struct nv50_fb_priv *priv = (void *)object; + + if (priv->r100c08_page) { + dma_unmap_page(nv_device_base(device), priv->r100c08, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(priv->r100c08_page); + } + + nvkm_fb_destroy(&priv->base); +} + +int +nv50_fb_init(struct nvkm_object *object) +{ + struct nv50_fb_impl *impl = (void *)object->oclass; + struct nv50_fb_priv *priv = (void *)object; + int ret; + + ret = nvkm_fb_init(&priv->base); + if (ret) + return ret; + + /* Not a clue what this is exactly. Without pointing it at a + * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) + * cause IOMMU "read from address 0" errors (rh#561267) + */ + nv_wr32(priv, 0x100c08, priv->r100c08 >> 8); + + /* This is needed to get meaningful information from 100c90 + * on traps. No idea what these values mean exactly. */ + nv_wr32(priv, 0x100c90, impl->trap); + return 0; +} + +struct nvkm_oclass * +nv50_fb_oclass = &(struct nv50_fb_impl) { + .base.base.handle = NV_SUBDEV(FB, 0x50), + .base.base.ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nvkm_fb_fini, + }, + .base.memtype = nv50_fb_memtype_valid, + .base.ram = &nv50_ram_oclass, + .trap = 0x000707ff, +}.base.base; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h new file mode 100644 index 000000000..f3cde3f1f --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h @@ -0,0 +1,31 @@ +#ifndef __NVKM_FB_NV50_H__ +#define __NVKM_FB_NV50_H__ +#include "priv.h" + +struct nv50_fb_priv { + struct nvkm_fb base; + struct page *r100c08_page; + dma_addr_t r100c08; +}; + +int nv50_fb_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +void nv50_fb_dtor(struct nvkm_object *); +int nv50_fb_init(struct nvkm_object *); + +struct nv50_fb_impl { + struct nvkm_fb_impl base; + u32 trap; +}; + +#define nv50_ram_create(p,e,o,d) \ + nv50_ram_create_((p), (e), (o), sizeof(**d), (void **)d) +int nv50_ram_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +int nv50_ram_get(struct nvkm_fb *, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nvkm_mem **); +void nv50_ram_put(struct nvkm_fb *, struct nvkm_mem **); +void __nv50_ram_put(struct nvkm_fb *, struct nvkm_mem *); +extern int nv50_fb_memtype[0x80]; +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h new file mode 100644 index 000000000..485c4b648 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h @@ -0,0 +1,73 @@ +#ifndef __NVKM_FB_PRIV_H__ +#define __NVKM_FB_PRIV_H__ +#include +struct nvkm_bios; + +#define nvkm_ram_create(p,e,o,d) \ + nvkm_object_create_((p), (e), (o), 0, sizeof(**d), (void **)d) +#define nvkm_ram_destroy(p) \ + nvkm_object_destroy(&(p)->base) +#define nvkm_ram_init(p) \ + nvkm_object_init(&(p)->base) +#define nvkm_ram_fini(p,s) \ + nvkm_object_fini(&(p)->base, (s)) + +#define nvkm_ram_create_(p,e,o,s,d) \ + nvkm_object_create_((p), (e), (o), 0, (s), (void **)d) +#define _nvkm_ram_dtor nvkm_object_destroy +#define _nvkm_ram_init nvkm_object_init +#define _nvkm_ram_fini nvkm_object_fini + +extern struct nvkm_oclass nv04_ram_oclass; +extern struct nvkm_oclass nv10_ram_oclass; +extern struct nvkm_oclass nv1a_ram_oclass; +extern struct nvkm_oclass nv20_ram_oclass; +extern struct nvkm_oclass nv40_ram_oclass; +extern struct nvkm_oclass nv41_ram_oclass; +extern struct nvkm_oclass nv44_ram_oclass; +extern struct nvkm_oclass nv49_ram_oclass; +extern struct nvkm_oclass nv4e_ram_oclass; +extern struct nvkm_oclass nv50_ram_oclass; +extern struct nvkm_oclass gt215_ram_oclass; +extern struct nvkm_oclass mcp77_ram_oclass; +extern struct nvkm_oclass gf100_ram_oclass; +extern struct nvkm_oclass gk104_ram_oclass; +extern struct nvkm_oclass gm107_ram_oclass; + +int nvkm_sddr2_calc(struct nvkm_ram *ram); +int nvkm_sddr3_calc(struct nvkm_ram *ram); +int nvkm_gddr3_calc(struct nvkm_ram *ram); +int nvkm_gddr5_calc(struct nvkm_ram *ram, bool nuts); + +#define nvkm_fb_create(p,e,c,d) \ + nvkm_fb_create_((p), (e), (c), sizeof(**d), (void **)d) +#define nvkm_fb_destroy(p) ({ \ + struct nvkm_fb *pfb = (p); \ + _nvkm_fb_dtor(nv_object(pfb)); \ +}) +#define nvkm_fb_init(p) ({ \ + struct nvkm_fb *pfb = (p); \ + _nvkm_fb_init(nv_object(pfb)); \ +}) +#define nvkm_fb_fini(p,s) ({ \ + struct nvkm_fb *pfb = (p); \ + _nvkm_fb_fini(nv_object(pfb), (s)); \ +}) + +int nvkm_fb_create_(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, int, void **); +void _nvkm_fb_dtor(struct nvkm_object *); +int _nvkm_fb_init(struct nvkm_object *); +int _nvkm_fb_fini(struct nvkm_object *, bool); + +struct nvkm_fb_impl { + struct nvkm_oclass base; + struct nvkm_oclass *ram; + bool (*memtype)(struct nvkm_fb *, u32); +}; + +bool nv04_fb_memtype_valid(struct nvkm_fb *, u32 memtype); +bool nv50_fb_memtype_valid(struct nvkm_fb *, u32 memtype); + +int nvkm_fb_bios_memtype(struct nvkm_bios *); +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h new file mode 100644 index 000000000..f343682b1 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h @@ -0,0 +1,180 @@ +#ifndef __NVKM_FBRAM_FUC_H__ +#define __NVKM_FBRAM_FUC_H__ +#include + +struct ramfuc { + struct nvkm_memx *memx; + struct nvkm_fb *pfb; + int sequence; +}; + +struct ramfuc_reg { + int sequence; + bool force; + u32 addr; + u32 stride; /* in bytes */ + u32 mask; + u32 data; +}; + +static inline struct ramfuc_reg +ramfuc_stride(u32 addr, u32 stride, u32 mask) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = stride, + .mask = mask, + .data = 0xdeadbeef, + }; +} + +static inline struct ramfuc_reg +ramfuc_reg2(u32 addr1, u32 addr2) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr1, + .stride = addr2 - addr1, + .mask = 0x3, + .data = 0xdeadbeef, + }; +} + +static noinline struct ramfuc_reg +ramfuc_reg(u32 addr) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = 0, + .mask = 0x1, + .data = 0xdeadbeef, + }; +} + +static inline int +ramfuc_init(struct ramfuc *ram, struct nvkm_fb *pfb) +{ + struct nvkm_pmu *pmu = nvkm_pmu(pfb); + int ret; + + ret = nvkm_memx_init(pmu, &ram->memx); + if (ret) + return ret; + + ram->sequence++; + ram->pfb = pfb; + return 0; +} + +static inline int +ramfuc_exec(struct ramfuc *ram, bool exec) +{ + int ret = 0; + if (ram->pfb) { + ret = nvkm_memx_fini(&ram->memx, exec); + ram->pfb = NULL; + } + return ret; +} + +static inline u32 +ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg) +{ + if (reg->sequence != ram->sequence) + reg->data = nv_rd32(ram->pfb, reg->addr); + return reg->data; +} + +static inline void +ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data) +{ + unsigned int mask, off = 0; + + reg->sequence = ram->sequence; + reg->data = data; + + for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) { + if (mask & 1) + nvkm_memx_wr32(ram->memx, reg->addr+off, reg->data); + off += reg->stride; + } +} + +static inline void +ramfuc_nuke(struct ramfuc *ram, struct ramfuc_reg *reg) +{ + reg->force = true; +} + +static inline u32 +ramfuc_mask(struct ramfuc *ram, struct ramfuc_reg *reg, u32 mask, u32 data) +{ + u32 temp = ramfuc_rd32(ram, reg); + if (temp != ((temp & ~mask) | data) || reg->force) { + ramfuc_wr32(ram, reg, (temp & ~mask) | data); + reg->force = false; + } + return temp; +} + +static inline void +ramfuc_wait(struct ramfuc *ram, u32 addr, u32 mask, u32 data, u32 nsec) +{ + nvkm_memx_wait(ram->memx, addr, mask, data, nsec); +} + +static inline void +ramfuc_nsec(struct ramfuc *ram, u32 nsec) +{ + nvkm_memx_nsec(ram->memx, nsec); +} + +static inline void +ramfuc_wait_vblank(struct ramfuc *ram) +{ + nvkm_memx_wait_vblank(ram->memx); +} + +static inline void +ramfuc_train(struct ramfuc *ram) +{ + nvkm_memx_train(ram->memx); +} + +static inline int +ramfuc_train_result(struct nvkm_fb *pfb, u32 *result, u32 rsize) +{ + struct nvkm_pmu *pmu = nvkm_pmu(pfb); + + return nvkm_memx_train_result(pmu, result, rsize); +} + +static inline void +ramfuc_block(struct ramfuc *ram) +{ + nvkm_memx_block(ram->memx); +} + +static inline void +ramfuc_unblock(struct ramfuc *ram) +{ + nvkm_memx_unblock(ram->memx); +} + +#define ram_init(s,p) ramfuc_init(&(s)->base, (p)) +#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e)) +#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r) +#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d)) +#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r) +#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n)) +#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n)) +#define ram_wait_vblank(s) ramfuc_wait_vblank(&(s)->base) +#define ram_train(s) ramfuc_train(&(s)->base) +#define ram_train_result(s,r,l) ramfuc_train_result((s), (r), (l)) +#define ram_block(s) ramfuc_block(&(s)->base) +#define ram_unblock(s) ramfuc_unblock(&(s)->base) +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c new file mode 100644 index 000000000..de9f39569 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c @@ -0,0 +1,731 @@ +/* + * Copyright 2013 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 "gf100.h" +#include "ramfuc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gf100_ramfuc { + struct ramfuc base; + + struct ramfuc_reg r_0x10fe20; + struct ramfuc_reg r_0x10fe24; + struct ramfuc_reg r_0x137320; + struct ramfuc_reg r_0x137330; + + struct ramfuc_reg r_0x132000; + struct ramfuc_reg r_0x132004; + struct ramfuc_reg r_0x132100; + + struct ramfuc_reg r_0x137390; + + struct ramfuc_reg r_0x10f290; + struct ramfuc_reg r_0x10f294; + struct ramfuc_reg r_0x10f298; + struct ramfuc_reg r_0x10f29c; + struct ramfuc_reg r_0x10f2a0; + + struct ramfuc_reg r_0x10f300; + struct ramfuc_reg r_0x10f338; + struct ramfuc_reg r_0x10f340; + struct ramfuc_reg r_0x10f344; + struct ramfuc_reg r_0x10f348; + + struct ramfuc_reg r_0x10f910; + struct ramfuc_reg r_0x10f914; + + struct ramfuc_reg r_0x100b0c; + struct ramfuc_reg r_0x10f050; + struct ramfuc_reg r_0x10f090; + struct ramfuc_reg r_0x10f200; + struct ramfuc_reg r_0x10f210; + struct ramfuc_reg r_0x10f310; + struct ramfuc_reg r_0x10f314; + struct ramfuc_reg r_0x10f610; + struct ramfuc_reg r_0x10f614; + struct ramfuc_reg r_0x10f800; + struct ramfuc_reg r_0x10f808; + struct ramfuc_reg r_0x10f824; + struct ramfuc_reg r_0x10f830; + struct ramfuc_reg r_0x10f988; + struct ramfuc_reg r_0x10f98c; + struct ramfuc_reg r_0x10f990; + struct ramfuc_reg r_0x10f998; + struct ramfuc_reg r_0x10f9b0; + struct ramfuc_reg r_0x10f9b4; + struct ramfuc_reg r_0x10fb04; + struct ramfuc_reg r_0x10fb08; + struct ramfuc_reg r_0x137300; + struct ramfuc_reg r_0x137310; + struct ramfuc_reg r_0x137360; + struct ramfuc_reg r_0x1373ec; + struct ramfuc_reg r_0x1373f0; + struct ramfuc_reg r_0x1373f8; + + struct ramfuc_reg r_0x61c140; + struct ramfuc_reg r_0x611200; + + struct ramfuc_reg r_0x13d8f4; +}; + +struct gf100_ram { + struct nvkm_ram base; + struct gf100_ramfuc fuc; + struct nvbios_pll refpll; + struct nvbios_pll mempll; +}; + +static void +gf100_ram_train(struct gf100_ramfuc *fuc, u32 magic) +{ + struct gf100_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nvkm_fb *pfb = nvkm_fb(ram); + u32 part = nv_rd32(pfb, 0x022438), i; + u32 mask = nv_rd32(pfb, 0x022554); + u32 addr = 0x110974; + + ram_wr32(fuc, 0x10f910, magic); + ram_wr32(fuc, 0x10f914, magic); + + for (i = 0; (magic & 0x80000000) && i < part; addr += 0x1000, i++) { + if (mask & (1 << i)) + continue; + ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000); + } +} + +static int +gf100_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_clk *clk = nvkm_clk(pfb); + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gf100_ram *ram = (void *)pfb->ram; + struct gf100_ramfuc *fuc = &ram->fuc; + struct nvbios_ramcfg cfg; + u8 ver, cnt, len, strap; + struct { + u32 data; + u8 size; + } rammap, ramcfg, timing; + int ref, div, out; + int from, mode; + int N1, M1, P; + int ret; + + /* lookup memory config data relevant to the target frequency */ + rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size, + &cnt, &ramcfg.size, &cfg); + if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) { + nv_error(pfb, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(nv_subdev(pfb)); + if (strap >= cnt) { + nv_error(pfb, "invalid ramcfg strap\n"); + return -EINVAL; + } + + ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size); + if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) { + nv_error(pfb, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + strap = nv_ro08(bios, ramcfg.data + 0x01); + if (strap != 0xff) { + timing.data = nvbios_timingEe(bios, strap, &ver, &timing.size, + &cnt, &len); + if (!timing.data || ver != 0x10 || timing.size < 0x19) { + nv_error(pfb, "invalid/missing timing entry\n"); + return -EINVAL; + } + } else { + timing.data = 0; + } + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + /* determine current mclk configuration */ + from = !!(ram_rd32(fuc, 0x1373f0) & 0x00000002); /*XXX: ok? */ + + /* determine target mclk configuration */ + if (!(ram_rd32(fuc, 0x137300) & 0x00000100)) + ref = clk->read(clk, nv_clk_src_sppll0); + else + ref = clk->read(clk, nv_clk_src_sppll1); + div = max(min((ref * 2) / freq, (u32)65), (u32)2) - 2; + out = (ref * 2) / (div + 2); + mode = freq != out; + + ram_mask(fuc, 0x137360, 0x00000002, 0x00000000); + + if ((ram_rd32(fuc, 0x132000) & 0x00000002) || 0 /*XXX*/) { + ram_nuke(fuc, 0x132000); + ram_mask(fuc, 0x132000, 0x00000002, 0x00000002); + ram_mask(fuc, 0x132000, 0x00000002, 0x00000000); + } + + if (mode == 1) { + ram_nuke(fuc, 0x10fe20); + ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000002); + ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000000); + } + +// 0x00020034 // 0x0000000a + ram_wr32(fuc, 0x132100, 0x00000001); + + if (mode == 1 && from == 0) { + /* calculate refpll */ + ret = gt215_pll_calc(nv_subdev(pfb), &ram->refpll, + ram->mempll.refclk, &N1, NULL, &M1, &P); + if (ret <= 0) { + nv_error(pfb, "unable to calc refpll\n"); + return ret ? ret : -ERANGE; + } + + ram_wr32(fuc, 0x10fe20, 0x20010000); + ram_wr32(fuc, 0x137320, 0x00000003); + ram_wr32(fuc, 0x137330, 0x81200006); + ram_wr32(fuc, 0x10fe24, (P << 16) | (N1 << 8) | M1); + ram_wr32(fuc, 0x10fe20, 0x20010001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + + /* calculate mempll */ + ret = gt215_pll_calc(nv_subdev(pfb), &ram->mempll, freq, + &N1, NULL, &M1, &P); + if (ret <= 0) { + nv_error(pfb, "unable to calc refpll\n"); + return ret ? ret : -ERANGE; + } + + ram_wr32(fuc, 0x10fe20, 0x20010005); + ram_wr32(fuc, 0x132004, (P << 16) | (N1 << 8) | M1); + ram_wr32(fuc, 0x132000, 0x18010101); + ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000); + } else + if (mode == 0) { + ram_wr32(fuc, 0x137300, 0x00000003); + } + + if (from == 0) { + ram_nuke(fuc, 0x10fb04); + ram_mask(fuc, 0x10fb04, 0x0000ffff, 0x00000000); + ram_nuke(fuc, 0x10fb08); + ram_mask(fuc, 0x10fb08, 0x0000ffff, 0x00000000); + ram_wr32(fuc, 0x10f988, 0x2004ff00); + ram_wr32(fuc, 0x10f98c, 0x003fc040); + ram_wr32(fuc, 0x10f990, 0x20012001); + ram_wr32(fuc, 0x10f998, 0x00011a00); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + } else { + ram_wr32(fuc, 0x10f988, 0x20010000); + ram_wr32(fuc, 0x10f98c, 0x00000000); + ram_wr32(fuc, 0x10f990, 0x20012001); + ram_wr32(fuc, 0x10f998, 0x00010a00); + } + + if (from == 0) { +// 0x00020039 // 0x000000ba + } + +// 0x0002003a // 0x00000002 + ram_wr32(fuc, 0x100b0c, 0x00080012); +// 0x00030014 // 0x00000000 // 0x02b5f070 +// 0x00030014 // 0x00010000 // 0x02b5f070 + ram_wr32(fuc, 0x611200, 0x00003300); +// 0x00020034 // 0x0000000a +// 0x00030020 // 0x00000001 // 0x00000000 + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x10f210, 0x00000000); + ram_nsec(fuc, 1000); + if (mode == 0) + gf100_ram_train(fuc, 0x000c1001); + ram_wr32(fuc, 0x10f310, 0x00000001); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f090, 0x00000061); + ram_wr32(fuc, 0x10f090, 0xc000007f); + ram_nsec(fuc, 1000); + + if (from == 0) { + ram_wr32(fuc, 0x10f824, 0x00007fd4); + } else { + ram_wr32(fuc, 0x1373ec, 0x00020404); + } + + if (mode == 0) { + ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000); + ram_mask(fuc, 0x10f200, 0x00008000, 0x00008000); + ram_wr32(fuc, 0x10f830, 0x41500010); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + ram_mask(fuc, 0x132100, 0x00000100, 0x00000100); + ram_wr32(fuc, 0x10f050, 0xff000090); + ram_wr32(fuc, 0x1373ec, 0x00020f0f); + ram_wr32(fuc, 0x1373f0, 0x00000003); + ram_wr32(fuc, 0x137310, 0x81201616); + ram_wr32(fuc, 0x132100, 0x00000001); +// 0x00020039 // 0x000000ba + ram_wr32(fuc, 0x10f830, 0x00300017); + ram_wr32(fuc, 0x1373f0, 0x00000001); + ram_wr32(fuc, 0x10f824, 0x00007e77); + ram_wr32(fuc, 0x132000, 0x18030001); + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); + ram_wr32(fuc, 0x10f210, 0x80000000); + ram_wr32(fuc, 0x10f338, 0x00300220); + ram_wr32(fuc, 0x10f300, 0x0000011d); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f290, 0x02060505); + ram_wr32(fuc, 0x10f294, 0x34208288); + ram_wr32(fuc, 0x10f298, 0x44050411); + ram_wr32(fuc, 0x10f29c, 0x0000114c); + ram_wr32(fuc, 0x10f2a0, 0x42e10069); + ram_wr32(fuc, 0x10f614, 0x40044f77); + ram_wr32(fuc, 0x10f610, 0x40044f77); + ram_wr32(fuc, 0x10f344, 0x00600009); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f348, 0x00700008); + ram_wr32(fuc, 0x61c140, 0x19240000); + ram_wr32(fuc, 0x10f830, 0x00300017); + gf100_ram_train(fuc, 0x80021001); + gf100_ram_train(fuc, 0x80081001); + ram_wr32(fuc, 0x10f340, 0x00500004); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f830, 0x01300017); + ram_wr32(fuc, 0x10f830, 0x00300017); +// 0x00030020 // 0x00000000 // 0x00000000 +// 0x00020034 // 0x0000000b + ram_wr32(fuc, 0x100b0c, 0x00080028); + ram_wr32(fuc, 0x611200, 0x00003330); + } else { + ram_wr32(fuc, 0x10f800, 0x00001800); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x1373ec, 0x00020404); + ram_wr32(fuc, 0x1373f0, 0x00000003); + ram_wr32(fuc, 0x10f830, 0x40700010); + ram_wr32(fuc, 0x10f830, 0x40500010); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x1373f8, 0x00000000); + ram_wr32(fuc, 0x132100, 0x00000101); + ram_wr32(fuc, 0x137310, 0x89201616); + ram_wr32(fuc, 0x10f050, 0xff000090); + ram_wr32(fuc, 0x1373ec, 0x00030404); + ram_wr32(fuc, 0x1373f0, 0x00000002); + // 0x00020039 // 0x00000011 + ram_wr32(fuc, 0x132100, 0x00000001); + ram_wr32(fuc, 0x1373f8, 0x00002000); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f808, 0x7aaa0050); + ram_wr32(fuc, 0x10f830, 0x00500010); + ram_wr32(fuc, 0x10f200, 0x00ce1000); + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); + ram_wr32(fuc, 0x10f210, 0x80000000); + ram_wr32(fuc, 0x10f338, 0x00300200); + ram_wr32(fuc, 0x10f300, 0x0000084d); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f290, 0x0b343825); + ram_wr32(fuc, 0x10f294, 0x3483028e); + ram_wr32(fuc, 0x10f298, 0x440c0600); + ram_wr32(fuc, 0x10f29c, 0x0000214c); + ram_wr32(fuc, 0x10f2a0, 0x42e20069); + ram_wr32(fuc, 0x10f200, 0x00ce0000); + ram_wr32(fuc, 0x10f614, 0x60044e77); + ram_wr32(fuc, 0x10f610, 0x60044e77); + ram_wr32(fuc, 0x10f340, 0x00500000); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f344, 0x00600228); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f348, 0x00700000); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x61c140, 0x09a40000); + + gf100_ram_train(fuc, 0x800e1008); + + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f800, 0x00001804); + // 0x00030020 // 0x00000000 // 0x00000000 + // 0x00020034 // 0x0000000b + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x100b0c, 0x00080028); + ram_wr32(fuc, 0x611200, 0x00003330); + ram_nsec(fuc, 100000); + ram_wr32(fuc, 0x10f9b0, 0x05313f41); + ram_wr32(fuc, 0x10f9b4, 0x00002f50); + + gf100_ram_train(fuc, 0x010c1001); + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000800); +// 0x00020016 // 0x00000000 + + if (mode == 0) + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + + return 0; +} + +static int +gf100_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct gf100_ram *ram = (void *)pfb->ram; + struct gf100_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, nvkm_boolopt(device->cfgopt, "NvMemExec", true)); + return 0; +} + +static void +gf100_ram_tidy(struct nvkm_fb *pfb) +{ + struct gf100_ram *ram = (void *)pfb->ram; + struct gf100_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, false); +} + +extern const u8 gf100_pte_storage_type_map[256]; + +void +gf100_ram_put(struct nvkm_fb *pfb, struct nvkm_mem **pmem) +{ + struct nvkm_ltc *ltc = nvkm_ltc(pfb); + struct nvkm_mem *mem = *pmem; + + *pmem = NULL; + if (unlikely(mem == NULL)) + return; + + mutex_lock(&pfb->base.mutex); + if (mem->tag) + ltc->tags_free(ltc, &mem->tag); + __nv50_ram_put(pfb, mem); + mutex_unlock(&pfb->base.mutex); + + kfree(mem); +} + +int +gf100_ram_get(struct nvkm_fb *pfb, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nvkm_mem **pmem) +{ + struct nvkm_mm *mm = &pfb->vram; + struct nvkm_mm_node *r; + struct nvkm_mem *mem; + int type = (memtype & 0x0ff); + int back = (memtype & 0x800); + const bool comp = gf100_pte_storage_type_map[type] != type; + int ret; + + size >>= 12; + align >>= 12; + ncmin >>= 12; + if (!ncmin) + ncmin = size; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + INIT_LIST_HEAD(&mem->regions); + mem->size = size; + + mutex_lock(&pfb->base.mutex); + if (comp) { + struct nvkm_ltc *ltc = nvkm_ltc(pfb); + + /* compression only works with lpages */ + if (align == (1 << (17 - 12))) { + int n = size >> 5; + ltc->tags_alloc(ltc, n, &mem->tag); + } + + if (unlikely(!mem->tag)) + type = gf100_pte_storage_type_map[type]; + } + mem->memtype = type; + + do { + if (back) + ret = nvkm_mm_tail(mm, 0, 1, size, ncmin, align, &r); + else + ret = nvkm_mm_head(mm, 0, 1, size, ncmin, align, &r); + if (ret) { + mutex_unlock(&pfb->base.mutex); + pfb->ram->put(pfb, &mem); + return ret; + } + + list_add_tail(&r->rl_entry, &mem->regions); + size -= r->length; + } while (size); + mutex_unlock(&pfb->base.mutex); + + r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry); + mem->offset = (u64)r->offset << 12; + *pmem = mem; + return 0; +} + +int +gf100_ram_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 maskaddr, int size, + void **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvkm_ram *ram; + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 parts = nv_rd32(pfb, 0x022438); + u32 pmask = nv_rd32(pfb, maskaddr); + u32 bsize = nv_rd32(pfb, 0x10f20c); + u32 offset, length; + bool uniform = true; + int ret, part; + + ret = nvkm_ram_create_(parent, engine, oclass, size, pobject); + ram = *pobject; + if (ret) + return ret; + + nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800)); + nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask); + + ram->type = nvkm_fb_bios_memtype(bios); + ram->ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1; + + /* read amount of vram attached to each memory controller */ + for (part = 0; part < parts; part++) { + if (!(pmask & (1 << part))) { + u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000)); + if (psize != bsize) { + if (psize < bsize) + bsize = psize; + uniform = false; + } + + nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize); + ram->size += (u64)psize << 20; + } + } + + /* if all controllers have the same amount attached, there's no holes */ + if (uniform) { + offset = rsvd_head; + length = (ram->size >> 12) - rsvd_head - rsvd_tail; + ret = nvkm_mm_init(&pfb->vram, offset, length, 1); + } else { + /* otherwise, address lowest common amount from 0GiB */ + ret = nvkm_mm_init(&pfb->vram, rsvd_head, + (bsize << 8) * parts - rsvd_head, 1); + if (ret) + return ret; + + /* and the rest starting from (8GiB + common_size) */ + offset = (0x0200000000ULL >> 12) + (bsize << 8); + length = (ram->size >> 12) - ((bsize * parts) << 8) - rsvd_tail; + + ret = nvkm_mm_init(&pfb->vram, offset, length, 1); + if (ret) + nvkm_mm_fini(&pfb->vram); + } + + if (ret) + return ret; + + ram->get = gf100_ram_get; + ram->put = gf100_ram_put; + return 0; +} + +static int +gf100_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object->parent; + struct gf100_ram *ram = (void *)object; + int ret, i; + + ret = nvkm_ram_init(&ram->base); + if (ret) + return ret; + + /* prepare for ddr link training, and load training patterns */ + switch (ram->base.type) { + case NV_MEM_TYPE_GDDR5: { + static const u8 train0[] = { + 0x00, 0xff, 0x55, 0xaa, 0x33, 0xcc, + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + }; + static const u32 train1[] = { + 0x00000000, 0xffffffff, + 0x55555555, 0xaaaaaaaa, + 0x33333333, 0xcccccccc, + 0xf0f0f0f0, 0x0f0f0f0f, + 0x00ff00ff, 0xff00ff00, + 0x0000ffff, 0xffff0000, + }; + + for (i = 0; i < 0x30; i++) { + nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]); + nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]); + nv_wr32(pfb, 0x10f918, train1[i % 12]); + nv_wr32(pfb, 0x10f91c, train1[i % 12]); + nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]); + nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]); + nv_wr32(pfb, 0x10f918, train1[i % 12]); + nv_wr32(pfb, 0x10f91c, train1[i % 12]); + } + } break; + default: + break; + } + + return 0; +} + +static int +gf100_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_bios *bios = nvkm_bios(parent); + struct gf100_ram *ram; + int ret; + + ret = gf100_ram_create(parent, engine, oclass, 0x022554, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + ret = nvbios_pll_parse(bios, 0x0c, &ram->refpll); + if (ret) { + nv_error(ram, "mclk refpll data not found\n"); + return ret; + } + + ret = nvbios_pll_parse(bios, 0x04, &ram->mempll); + if (ret) { + nv_error(ram, "mclk pll data not found\n"); + return ret; + } + + switch (ram->base.type) { + case NV_MEM_TYPE_GDDR5: + ram->base.calc = gf100_ram_calc; + ram->base.prog = gf100_ram_prog; + ram->base.tidy = gf100_ram_tidy; + break; + default: + nv_warn(ram, "reclocking of this ram type unsupported\n"); + return 0; + } + + ram->fuc.r_0x10fe20 = ramfuc_reg(0x10fe20); + ram->fuc.r_0x10fe24 = ramfuc_reg(0x10fe24); + ram->fuc.r_0x137320 = ramfuc_reg(0x137320); + ram->fuc.r_0x137330 = ramfuc_reg(0x137330); + + ram->fuc.r_0x132000 = ramfuc_reg(0x132000); + ram->fuc.r_0x132004 = ramfuc_reg(0x132004); + ram->fuc.r_0x132100 = ramfuc_reg(0x132100); + + ram->fuc.r_0x137390 = ramfuc_reg(0x137390); + + ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290); + ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294); + ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298); + ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c); + ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0); + + ram->fuc.r_0x10f300 = ramfuc_reg(0x10f300); + ram->fuc.r_0x10f338 = ramfuc_reg(0x10f338); + ram->fuc.r_0x10f340 = ramfuc_reg(0x10f340); + ram->fuc.r_0x10f344 = ramfuc_reg(0x10f344); + ram->fuc.r_0x10f348 = ramfuc_reg(0x10f348); + + ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910); + ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914); + + ram->fuc.r_0x100b0c = ramfuc_reg(0x100b0c); + ram->fuc.r_0x10f050 = ramfuc_reg(0x10f050); + ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090); + ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200); + ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210); + ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310); + ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314); + ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610); + ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614); + ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800); + ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808); + ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824); + ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830); + ram->fuc.r_0x10f988 = ramfuc_reg(0x10f988); + ram->fuc.r_0x10f98c = ramfuc_reg(0x10f98c); + ram->fuc.r_0x10f990 = ramfuc_reg(0x10f990); + ram->fuc.r_0x10f998 = ramfuc_reg(0x10f998); + ram->fuc.r_0x10f9b0 = ramfuc_reg(0x10f9b0); + ram->fuc.r_0x10f9b4 = ramfuc_reg(0x10f9b4); + ram->fuc.r_0x10fb04 = ramfuc_reg(0x10fb04); + ram->fuc.r_0x10fb08 = ramfuc_reg(0x10fb08); + ram->fuc.r_0x137310 = ramfuc_reg(0x137300); + ram->fuc.r_0x137310 = ramfuc_reg(0x137310); + ram->fuc.r_0x137360 = ramfuc_reg(0x137360); + ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec); + ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0); + ram->fuc.r_0x1373f8 = ramfuc_reg(0x1373f8); + + ram->fuc.r_0x61c140 = ramfuc_reg(0x61c140); + ram->fuc.r_0x611200 = ramfuc_reg(0x611200); + + ram->fuc.r_0x13d8f4 = ramfuc_reg(0x13d8f4); + return 0; +} + +struct nvkm_oclass +gf100_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gf100_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = gf100_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c new file mode 100644 index 000000000..1ef15c3e6 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c @@ -0,0 +1,1639 @@ +/* + * Copyright 2013 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 "ramfuc.h" +#include "gf100.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gk104_ramfuc { + struct ramfuc base; + + struct nvbios_pll refpll; + struct nvbios_pll mempll; + + struct ramfuc_reg r_gpioMV; + u32 r_funcMV[2]; + struct ramfuc_reg r_gpio2E; + u32 r_func2E[2]; + struct ramfuc_reg r_gpiotrig; + + struct ramfuc_reg r_0x132020; + struct ramfuc_reg r_0x132028; + struct ramfuc_reg r_0x132024; + struct ramfuc_reg r_0x132030; + struct ramfuc_reg r_0x132034; + struct ramfuc_reg r_0x132000; + struct ramfuc_reg r_0x132004; + struct ramfuc_reg r_0x132040; + + struct ramfuc_reg r_0x10f248; + struct ramfuc_reg r_0x10f290; + struct ramfuc_reg r_0x10f294; + struct ramfuc_reg r_0x10f298; + struct ramfuc_reg r_0x10f29c; + struct ramfuc_reg r_0x10f2a0; + struct ramfuc_reg r_0x10f2a4; + struct ramfuc_reg r_0x10f2a8; + struct ramfuc_reg r_0x10f2ac; + struct ramfuc_reg r_0x10f2cc; + struct ramfuc_reg r_0x10f2e8; + struct ramfuc_reg r_0x10f250; + struct ramfuc_reg r_0x10f24c; + struct ramfuc_reg r_0x10fec4; + struct ramfuc_reg r_0x10fec8; + struct ramfuc_reg r_0x10f604; + struct ramfuc_reg r_0x10f614; + struct ramfuc_reg r_0x10f610; + struct ramfuc_reg r_0x100770; + struct ramfuc_reg r_0x100778; + struct ramfuc_reg r_0x10f224; + + struct ramfuc_reg r_0x10f870; + struct ramfuc_reg r_0x10f698; + struct ramfuc_reg r_0x10f694; + struct ramfuc_reg r_0x10f6b8; + struct ramfuc_reg r_0x10f808; + struct ramfuc_reg r_0x10f670; + struct ramfuc_reg r_0x10f60c; + struct ramfuc_reg r_0x10f830; + struct ramfuc_reg r_0x1373ec; + struct ramfuc_reg r_0x10f800; + struct ramfuc_reg r_0x10f82c; + + struct ramfuc_reg r_0x10f978; + struct ramfuc_reg r_0x10f910; + struct ramfuc_reg r_0x10f914; + + struct ramfuc_reg r_mr[16]; /* MR0 - MR8, MR15 */ + + struct ramfuc_reg r_0x62c000; + + struct ramfuc_reg r_0x10f200; + + struct ramfuc_reg r_0x10f210; + struct ramfuc_reg r_0x10f310; + struct ramfuc_reg r_0x10f314; + struct ramfuc_reg r_0x10f318; + struct ramfuc_reg r_0x10f090; + struct ramfuc_reg r_0x10f69c; + struct ramfuc_reg r_0x10f824; + struct ramfuc_reg r_0x1373f0; + struct ramfuc_reg r_0x1373f4; + struct ramfuc_reg r_0x137320; + struct ramfuc_reg r_0x10f65c; + struct ramfuc_reg r_0x10f6bc; + struct ramfuc_reg r_0x100710; + struct ramfuc_reg r_0x100750; +}; + +struct gk104_ram { + struct nvkm_ram base; + struct gk104_ramfuc fuc; + + struct list_head cfg; + u32 parts; + u32 pmask; + u32 pnuts; + + struct nvbios_ramcfg diff; + int from; + int mode; + int N1, fN1, M1, P1; + int N2, M2, P2; +}; + +/******************************************************************************* + * GDDR5 + ******************************************************************************/ +static void +gk104_ram_train(struct gk104_ramfuc *fuc, u32 mask, u32 data) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + u32 addr = 0x110974, i; + + ram_mask(fuc, 0x10f910, mask, data); + ram_mask(fuc, 0x10f914, mask, data); + + for (i = 0; (data & 0x80000000) && i < ram->parts; addr += 0x1000, i++) { + if (ram->pmask & (1 << i)) + continue; + ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000); + } +} + +static void +r1373f4_init(struct gk104_ramfuc *fuc) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + const u32 mcoef = ((--ram->P2 << 28) | (ram->N2 << 8) | ram->M2); + const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1); + const u32 runk0 = ram->fN1 << 16; + const u32 runk1 = ram->fN1; + + if (ram->from == 2) { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100); + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010); + } else { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010); + } + + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000); + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000); + + /* (re)program refpll, if required */ + if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef || + (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) { + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000000); + ram_wr32(fuc, 0x137320, 0x00000000); + ram_mask(fuc, 0x132030, 0xffff0000, runk0); + ram_mask(fuc, 0x132034, 0x0000ffff, runk1); + ram_wr32(fuc, 0x132024, rcoef); + ram_mask(fuc, 0x132028, 0x00080000, 0x00080000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x132028, 0x00080000, 0x00000000); + } + + /* (re)program mempll, if required */ + if (ram->mode == 2) { + ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + ram_mask(fuc, 0x132000, 0x80000000, 0x80000000); + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132004, 0x103fffff, mcoef); + ram_mask(fuc, 0x132000, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000); + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100); + } else { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010100); + } + + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010); +} + +static void +r1373f4_fini(struct gk104_ramfuc *fuc) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nvkm_ram_data *next = ram->base.next; + u8 v0 = next->bios.ramcfg_11_03_c0; + u8 v1 = next->bios.ramcfg_11_03_30; + u32 tmp; + + tmp = ram_rd32(fuc, 0x1373ec) & ~0x00030000; + ram_wr32(fuc, 0x1373ec, tmp | (v1 << 16)); + ram_mask(fuc, 0x1373f0, (~ram->mode & 3), 0x00000000); + if (ram->mode == 2) { + ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000002); + ram_mask(fuc, 0x1373f4, 0x00001100, 0x000000000); + } else { + ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000001); + ram_mask(fuc, 0x1373f4, 0x00010000, 0x000000000); + } + ram_mask(fuc, 0x10f800, 0x00000030, (v0 ^ v1) << 4); +} + +static void +gk104_ram_nuts(struct gk104_ram *ram, struct ramfuc_reg *reg, + u32 _mask, u32 _data, u32 _copy) +{ + struct gk104_fb_priv *priv = (void *)nvkm_fb(ram); + struct ramfuc *fuc = &ram->fuc.base; + u32 addr = 0x110000 + (reg->addr & 0xfff); + u32 mask = _mask | _copy; + u32 data = (_data & _mask) | (reg->data & _copy); + u32 i; + + for (i = 0; i < 16; i++, addr += 0x1000) { + if (ram->pnuts & (1 << i)) { + u32 prev = nv_rd32(priv, addr); + u32 next = (prev & ~mask) | data; + nvkm_memx_wr32(fuc->memx, addr, next); + } + } +} +#define ram_nuts(s,r,m,d,c) \ + gk104_ram_nuts((s), &(s)->fuc.r_##r, (m), (d), (c)) + +static int +gk104_ram_calc_gddr5(struct nvkm_fb *pfb, u32 freq) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + struct nvkm_ram_data *next = ram->base.next; + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0000); + + /* MR1: turn termination on early, for some reason.. */ + if ((ram->base.mr[1] & 0x03c) != 0x030) { + ram_mask(fuc, mr[1], 0x03c, ram->base.mr[1] & 0x03c); + ram_nuts(ram, mr[1], 0x03c, ram->base.mr1_nuts & 0x03c, 0x000); + } + + if (vc == 1 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + + gk104_ram_train(fuc, 0x01020000, 0x000c0000); + + ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */ + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_nsec(fuc, 1000); + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_wr32(fuc, 0x10f090, 0x00000061); + ram_wr32(fuc, 0x10f090, 0xc000007f); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f698, 0x00000000); + ram_wr32(fuc, 0x10f69c, 0x00000000); + + /*XXX: there does appear to be some kind of condition here, simply + * modifying these bits in the vbios from the default pl0 + * entries shows no change. however, the data does appear to + * be correct and may be required for the transition back + */ + mask = 0x800f07e0; + data = 0x00030000; + if (ram_rd32(fuc, 0x10f978) & 0x00800000) + data |= 0x00040000; + + if (1) { + data |= 0x800807e0; + switch (next->bios.ramcfg_11_03_c0) { + case 3: data &= ~0x00000040; break; + case 2: data &= ~0x00000100; break; + case 1: data &= ~0x80000000; break; + case 0: data &= ~0x00000400; break; + } + + switch (next->bios.ramcfg_11_03_30) { + case 3: data &= ~0x00000020; break; + case 2: data &= ~0x00000080; break; + case 1: data &= ~0x00080000; break; + case 0: data &= ~0x00000200; break; + } + } + + if (next->bios.ramcfg_11_02_80) + mask |= 0x03000000; + if (next->bios.ramcfg_11_02_40) + mask |= 0x00002000; + if (next->bios.ramcfg_11_07_10) + mask |= 0x00004000; + if (next->bios.ramcfg_11_07_08) + mask |= 0x00000003; + else { + mask |= 0x34000000; + if (ram_rd32(fuc, 0x10f978) & 0x00800000) + mask |= 0x40000000; + } + ram_mask(fuc, 0x10f824, mask, data); + + ram_mask(fuc, 0x132040, 0x00010000, 0x00000000); + + if (ram->from == 2 && ram->mode != 2) { + ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000); + ram_mask(fuc, 0x10f200, 0x18008000, 0x00008000); + ram_mask(fuc, 0x10f800, 0x00000000, 0x00000004); + ram_mask(fuc, 0x10f830, 0x00008000, 0x01040010); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + r1373f4_init(fuc); + ram_mask(fuc, 0x1373f0, 0x00000002, 0x00000001); + r1373f4_fini(fuc); + ram_mask(fuc, 0x10f830, 0x00c00000, 0x00240001); + } else + if (ram->from != 2 && ram->mode != 2) { + r1373f4_init(fuc); + r1373f4_fini(fuc); + } + + if (ram_have(fuc, gpioMV)) { + u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]); + if (temp != ram_rd32(fuc, gpioMV)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 64000); + } + } + + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->from != 2 && ram->mode == 2) { + if (0 /*XXX: Titan */) + ram_mask(fuc, 0x10f200, 0x18000000, 0x18000000); + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000); + ram_mask(fuc, 0x1373f0, 0x00000000, 0x00000002); + ram_mask(fuc, 0x10f830, 0x00800001, 0x00408010); + r1373f4_init(fuc); + r1373f4_fini(fuc); + ram_mask(fuc, 0x10f808, 0x00000000, 0x00080000); + ram_mask(fuc, 0x10f200, 0x00808000, 0x00800000); + } else + if (ram->from == 2 && ram->mode == 2) { + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000); + r1373f4_init(fuc); + r1373f4_fini(fuc); + } + + if (ram->mode != 2) /*XXX*/ { + if (next->bios.ramcfg_11_07_40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09); + + if (!next->bios.ramcfg_11_07_08 && !next->bios.ramcfg_11_07_04) { + ram_wr32(fuc, 0x10f698, 0x01010101 * next->bios.ramcfg_11_04); + ram_wr32(fuc, 0x10f69c, 0x01010101 * next->bios.ramcfg_11_04); + } else + if (!next->bios.ramcfg_11_07_08) { + ram_wr32(fuc, 0x10f698, 0x00000000); + ram_wr32(fuc, 0x10f69c, 0x00000000); + } + + if (ram->mode != 2) { + u32 data = 0x01000100 * next->bios.ramcfg_11_04; + ram_nuke(fuc, 0x10f694); + ram_mask(fuc, 0x10f694, 0xff00ff00, data); + } + + if (ram->mode == 2 && next->bios.ramcfg_11_08_10) + data = 0x00000080; + else + data = 0x00000000; + ram_mask(fuc, 0x10f60c, 0x00000080, data); + + mask = 0x00070000; + data = 0x00000000; + if (!next->bios.ramcfg_11_02_80) + data |= 0x03000000; + if (!next->bios.ramcfg_11_02_40) + data |= 0x00002000; + if (!next->bios.ramcfg_11_07_10) + data |= 0x00004000; + if (!next->bios.ramcfg_11_07_08) + data |= 0x00000003; + else + data |= 0x74000000; + ram_mask(fuc, 0x10f824, mask, data); + + if (next->bios.ramcfg_11_01_08) + data = 0x00000000; + else + data = 0x00001000; + ram_mask(fuc, 0x10f200, 0x00001000, data); + + if (ram_rd32(fuc, 0x10f670) & 0x80000000) { + ram_nsec(fuc, 10000); + ram_mask(fuc, 0x10f670, 0x80000000, 0x00000000); + } + + if (next->bios.ramcfg_11_08_01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + data = 0x00000000; + if (next->bios.ramcfg_11_08_08) + data |= 0x00002000; + if (next->bios.ramcfg_11_08_04) + data |= 0x00001000; + if (next->bios.ramcfg_11_08_02) + data |= 0x00004000; + ram_mask(fuc, 0x10f830, 0x00007000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]); + ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]); + ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]); + ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]); + ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]); + ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]); + ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]); + ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]); + ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]); + ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]); + ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_08_20) { + if (next->bios.ramcfg_11_08_20) + data |= 0x01000000; + mask |= 0x01000000; + } + ram_mask(fuc, 0x10f200, mask, data); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_02_03) { + data |= next->bios.ramcfg_11_02_03 << 8; + mask |= 0x00000300; + } + if (ram->diff.ramcfg_11_01_10) { + if (next->bios.ramcfg_11_01_10) + data |= 0x70000000; + mask |= 0x70000000; + } + ram_mask(fuc, 0x10f604, mask, data); + + data = mask = 0x00000000; + if (ram->diff.timing_20_30_07) { + data |= next->bios.timing_20_30_07 << 28; + mask |= 0x70000000; + } + if (ram->diff.ramcfg_11_01_01) { + if (next->bios.ramcfg_11_01_01) + data |= 0x00000100; + mask |= 0x00000100; + } + ram_mask(fuc, 0x10f614, mask, data); + + data = mask = 0x00000000; + if (ram->diff.timing_20_30_07) { + data |= next->bios.timing_20_30_07 << 28; + mask |= 0x70000000; + } + if (ram->diff.ramcfg_11_01_02) { + if (next->bios.ramcfg_11_01_02) + data |= 0x00000100; + mask |= 0x00000100; + } + ram_mask(fuc, 0x10f610, mask, data); + + mask = 0x33f00000; + data = 0x00000000; + if (!next->bios.ramcfg_11_01_04) + data |= 0x20200000; + if (!next->bios.ramcfg_11_07_80) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if (next->bios.ramcfg_11_03_f0) { + if (next->bios.rammap_11_08_0c) { + if (!next->bios.ramcfg_11_07_80) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x00000004; + } + } else { + mask |= 0x40000020; + data |= 0x00000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_02_03) { + data |= next->bios.ramcfg_11_02_03; + mask |= 0x00000003; + } + if (ram->diff.ramcfg_11_01_10) { + if (next->bios.ramcfg_11_01_10) + data |= 0x00000004; + mask |= 0x00000004; + } + + if ((ram_mask(fuc, 0x100770, mask, data) & mask & 4) != (data & 4)) { + ram_mask(fuc, 0x100750, 0x00000008, 0x00000008); + ram_wr32(fuc, 0x100710, 0x00000000); + ram_wait(fuc, 0x100710, 0x80000000, 0x80000000, 200000); + } + + data = next->bios.timing_20_30_07 << 8; + if (next->bios.ramcfg_11_01_01) + data |= 0x80000000; + ram_mask(fuc, 0x100778, 0x00000700, data); + + ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4); + data = (next->bios.timing[10] & 0x7f000000) >> 24; + if (data < next->bios.timing_20_2c_1fc0) + data = next->bios.timing_20_2c_1fc0; + ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); + ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16); + + ram_mask(fuc, 0x10fec4, 0x041e0f07, next->bios.timing_20_31_0800 << 26 | + next->bios.timing_20_31_0780 << 17 | + next->bios.timing_20_31_0078 << 8 | + next->bios.timing_20_31_0007); + ram_mask(fuc, 0x10fec8, 0x00000027, next->bios.timing_20_31_8000 << 5 | + next->bios.timing_20_31_7000); + + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ + + if (next->bios.ramcfg_11_08_10 && (ram->mode == 2) /*XXX*/) { + u32 temp = ram_mask(fuc, 0x10f294, 0xff000000, 0x24000000); + gk104_ram_train(fuc, 0xbc0e0000, 0xa4010000); /*XXX*/ + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f294, temp); + } + + ram_mask(fuc, mr[3], 0xfff, ram->base.mr[3]); + ram_wr32(fuc, mr[0], ram->base.mr[0]); + ram_mask(fuc, mr[8], 0xfff, ram->base.mr[8]); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[1], 0xfff, ram->base.mr[1]); + ram_mask(fuc, mr[5], 0xfff, ram->base.mr[5] & ~0x004); /* LP3 later */ + ram_mask(fuc, mr[6], 0xfff, ram->base.mr[6]); + ram_mask(fuc, mr[7], 0xfff, ram->base.mr[7]); + + if (vc == 0 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + ram_nuts(ram, 0x10f200, 0x18808800, 0x00000000, 0x18808800); + + data = ram_rd32(fuc, 0x10f978); + data &= ~0x00046144; + data |= 0x0000000b; + if (!next->bios.ramcfg_11_07_08) { + if (!next->bios.ramcfg_11_07_04) + data |= 0x0000200c; + else + data |= 0x00000000; + } else { + data |= 0x00040044; + } + ram_wr32(fuc, 0x10f978, data); + + if (ram->mode == 1) { + data = ram_rd32(fuc, 0x10f830) | 0x00000001; + ram_wr32(fuc, 0x10f830, data); + } + + if (!next->bios.ramcfg_11_07_08) { + data = 0x88020000; + if ( next->bios.ramcfg_11_07_04) + data |= 0x10000000; + if (!next->bios.rammap_11_08_10) + data |= 0x00080000; + } else { + data = 0xa40e0000; + } + gk104_ram_train(fuc, 0xbc0f0000, data); + if (1) /* XXX: not always? */ + ram_nsec(fuc, 1000); + + if (ram->mode == 2) { /*XXX*/ + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000004); + } + + /* LP3 */ + if (ram_mask(fuc, mr[5], 0x004, ram->base.mr[5]) != ram->base.mr[5]) + ram_nsec(fuc, 1000); + + if (ram->mode != 2) { + ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + } + + if (next->bios.ramcfg_11_07_02) + gk104_ram_train(fuc, 0x80020000, 0x01000000); + + ram_unblock(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (next->bios.rammap_11_08_01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + ram_nuts(ram, 0x10f200, 0x18808800, data, 0x18808800); + return 0; +} + +/******************************************************************************* + * DDR3 + ******************************************************************************/ + +static int +gk104_ram_calc_sddr3(struct nvkm_fb *pfb, u32 freq) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1); + const u32 runk0 = ram->fN1 << 16; + const u32 runk1 = ram->fN1; + struct nvkm_ram_data *next = ram->base.next; + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0000); + + if (vc == 1 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + if (next->bios.ramcfg_11_03_f0) + ram_mask(fuc, 0x10f808, 0x04000000, 0x04000000); + + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f090, 0x00000060); + ram_wr32(fuc, 0x10f090, 0xc000007e); + + /*XXX: there does appear to be some kind of condition here, simply + * modifying these bits in the vbios from the default pl0 + * entries shows no change. however, the data does appear to + * be correct and may be required for the transition back + */ + mask = 0x00010000; + data = 0x00010000; + + if (1) { + mask |= 0x800807e0; + data |= 0x800807e0; + switch (next->bios.ramcfg_11_03_c0) { + case 3: data &= ~0x00000040; break; + case 2: data &= ~0x00000100; break; + case 1: data &= ~0x80000000; break; + case 0: data &= ~0x00000400; break; + } + + switch (next->bios.ramcfg_11_03_30) { + case 3: data &= ~0x00000020; break; + case 2: data &= ~0x00000080; break; + case 1: data &= ~0x00080000; break; + case 0: data &= ~0x00000200; break; + } + } + + if (next->bios.ramcfg_11_02_80) + mask |= 0x03000000; + if (next->bios.ramcfg_11_02_40) + mask |= 0x00002000; + if (next->bios.ramcfg_11_07_10) + mask |= 0x00004000; + if (next->bios.ramcfg_11_07_08) + mask |= 0x00000003; + else + mask |= 0x14000000; + ram_mask(fuc, 0x10f824, mask, data); + + ram_mask(fuc, 0x132040, 0x00010000, 0x00000000); + + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010); + data = ram_rd32(fuc, 0x1373ec) & ~0x00030000; + data |= next->bios.ramcfg_11_03_30 << 16; + ram_wr32(fuc, 0x1373ec, data); + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000); + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000); + + /* (re)program refpll, if required */ + if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef || + (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) { + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000000); + ram_wr32(fuc, 0x137320, 0x00000000); + ram_mask(fuc, 0x132030, 0xffff0000, runk0); + ram_mask(fuc, 0x132034, 0x0000ffff, runk1); + ram_wr32(fuc, 0x132024, rcoef); + ram_mask(fuc, 0x132028, 0x00080000, 0x00080000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x132028, 0x00080000, 0x00000000); + } + + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000010); + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000001); + ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + + if (ram_have(fuc, gpioMV)) { + u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]); + if (temp != ram_rd32(fuc, gpioMV)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 64000); + } + } + + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->mode != 2) /*XXX*/ { + if (next->bios.ramcfg_11_07_40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09); + + mask = 0x00010000; + data = 0x00000000; + if (!next->bios.ramcfg_11_02_80) + data |= 0x03000000; + if (!next->bios.ramcfg_11_02_40) + data |= 0x00002000; + if (!next->bios.ramcfg_11_07_10) + data |= 0x00004000; + if (!next->bios.ramcfg_11_07_08) + data |= 0x00000003; + else + data |= 0x14000000; + ram_mask(fuc, 0x10f824, mask, data); + ram_nsec(fuc, 1000); + + if (next->bios.ramcfg_11_08_01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]); + ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]); + ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]); + ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]); + ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]); + ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]); + ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]); + ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]); + ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]); + ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]); + ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); + + mask = 0x33f00000; + data = 0x00000000; + if (!next->bios.ramcfg_11_01_04) + data |= 0x20200000; + if (!next->bios.ramcfg_11_07_80) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if (next->bios.ramcfg_11_03_f0) { + if (next->bios.rammap_11_08_0c) { + if (!next->bios.ramcfg_11_07_80) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x08000004; + } + data |= 0x04000000; + } else { + mask |= 0x44000020; + data |= 0x08000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); + + ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4); + + data = (next->bios.timing[10] & 0x7f000000) >> 24; + if (data < next->bios.timing_20_2c_1fc0) + data = next->bios.timing_20_2c_1fc0; + ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); + + ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16); + + ram_wr32(fuc, 0x10f090, 0x4000007f); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ + ram_nsec(fuc, 1000); + + ram_nuke(fuc, mr[0]); + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_mask(fuc, mr[0], 0x100, 0x000); + + ram_mask(fuc, mr[2], 0xfff, ram->base.mr[2]); + ram_wr32(fuc, mr[0], ram->base.mr[0]); + ram_nsec(fuc, 1000); + + ram_nuke(fuc, mr[0]); + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_mask(fuc, mr[0], 0x100, 0x000); + + if (vc == 0 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + if (ram->mode != 2) { + ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + } + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + + ram_unblock(fuc); + ram_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (next->bios.rammap_11_08_01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + return 0; +} + +/******************************************************************************* + * main hooks + ******************************************************************************/ + +static int +gk104_ram_calc_data(struct nvkm_fb *pfb, u32 khz, struct nvkm_ram_data *data) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct nvkm_ram_data *cfg; + u32 mhz = khz / 1000; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) { + *data = *cfg; + data->freq = khz; + return 0; + } + } + + nv_error(ram, "ramcfg data for %dMHz not found\n", mhz); + return -EINVAL; +} + +static int +gk104_ram_calc_xits(struct nvkm_fb *pfb, struct nvkm_ram_data *next) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + int refclk, i; + int ret; + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + ram->mode = (next->freq > fuc->refpll.vco1.max_freq) ? 2 : 1; + ram->from = ram_rd32(fuc, 0x1373f4) & 0x0000000f; + + /* XXX: this is *not* what nvidia do. on fermi nvidia generally + * select, based on some unknown condition, one of the two possible + * reference frequencies listed in the vbios table for mempll and + * program refpll to that frequency. + * + * so far, i've seen very weird values being chosen by nvidia on + * kepler boards, no idea how/why they're chosen. + */ + refclk = next->freq; + if (ram->mode == 2) + refclk = fuc->mempll.refclk; + + /* calculate refpll coefficients */ + ret = gt215_pll_calc(nv_subdev(pfb), &fuc->refpll, refclk, &ram->N1, + &ram->fN1, &ram->M1, &ram->P1); + fuc->mempll.refclk = ret; + if (ret <= 0) { + nv_error(pfb, "unable to calc refpll\n"); + return -EINVAL; + } + + /* calculate mempll coefficients, if we're using it */ + if (ram->mode == 2) { + /* post-divider doesn't work... the reg takes the values but + * appears to completely ignore it. there *is* a bit at + * bit 28 that appears to divide the clock by 2 if set. + */ + fuc->mempll.min_p = 1; + fuc->mempll.max_p = 2; + + ret = gt215_pll_calc(nv_subdev(pfb), &fuc->mempll, next->freq, + &ram->N2, NULL, &ram->M2, &ram->P2); + if (ret <= 0) { + nv_error(pfb, "unable to calc mempll\n"); + return -EINVAL; + } + } + + for (i = 0; i < ARRAY_SIZE(fuc->r_mr); i++) { + if (ram_have(fuc, mr[i])) + ram->base.mr[i] = ram_rd32(fuc, mr[i]); + } + ram->base.freq = next->freq; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + ret = nvkm_sddr3_calc(&ram->base); + if (ret == 0) + ret = gk104_ram_calc_sddr3(pfb, next->freq); + break; + case NV_MEM_TYPE_GDDR5: + ret = nvkm_gddr5_calc(&ram->base, ram->pnuts != 0); + if (ret == 0) + ret = gk104_ram_calc_gddr5(pfb, next->freq); + break; + default: + ret = -ENOSYS; + break; + } + + return ret; +} + +static int +gk104_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_clk *clk = nvkm_clk(pfb); + struct gk104_ram *ram = (void *)pfb->ram; + struct nvkm_ram_data *xits = &ram->base.xition; + struct nvkm_ram_data *copy; + int ret; + + if (ram->base.next == NULL) { + ret = gk104_ram_calc_data(pfb, clk->read(clk, nv_clk_src_mem), + &ram->base.former); + if (ret) + return ret; + + ret = gk104_ram_calc_data(pfb, freq, &ram->base.target); + if (ret) + return ret; + + if (ram->base.target.freq < ram->base.former.freq) { + *xits = ram->base.target; + copy = &ram->base.former; + } else { + *xits = ram->base.former; + copy = &ram->base.target; + } + + xits->bios.ramcfg_11_02_04 = copy->bios.ramcfg_11_02_04; + xits->bios.ramcfg_11_02_03 = copy->bios.ramcfg_11_02_03; + xits->bios.timing_20_30_07 = copy->bios.timing_20_30_07; + + ram->base.next = &ram->base.target; + if (memcmp(xits, &ram->base.former, sizeof(xits->bios))) + ram->base.next = &ram->base.xition; + } else { + BUG_ON(ram->base.next != &ram->base.xition); + ram->base.next = &ram->base.target; + } + + return gk104_ram_calc_xits(pfb, ram->base.next); +} + +static void +gk104_ram_prog_0(struct nvkm_fb *pfb, u32 freq) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct nvkm_ram_data *cfg; + u32 mhz = freq / 1000; + u32 mask, data; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) + break; + } + + if (&cfg->head == &ram->cfg) + return; + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_03fe) { + data |= cfg->bios.rammap_11_0a_03fe << 12; + mask |= 0x001ff000; + } + if (ram->diff.rammap_11_09_01ff) { + data |= cfg->bios.rammap_11_09_01ff; + mask |= 0x000001ff; + } + nv_mask(pfb, 0x10f468, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0400) { + data |= cfg->bios.rammap_11_0a_0400; + mask |= 0x00000001; + } + nv_mask(pfb, 0x10f420, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0800) { + data |= cfg->bios.rammap_11_0a_0800; + mask |= 0x00000001; + } + nv_mask(pfb, 0x10f430, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_01f0) { + data |= cfg->bios.rammap_11_0b_01f0; + mask |= 0x0000001f; + } + nv_mask(pfb, 0x10f400, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_0200) { + data |= cfg->bios.rammap_11_0b_0200 << 9; + mask |= 0x00000200; + } + nv_mask(pfb, 0x10f410, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0d) { + data |= cfg->bios.rammap_11_0d << 16; + mask |= 0x00ff0000; + } + if (ram->diff.rammap_11_0f) { + data |= cfg->bios.rammap_11_0f << 8; + mask |= 0x0000ff00; + } + nv_mask(pfb, 0x10f440, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0e) { + data |= cfg->bios.rammap_11_0e << 8; + mask |= 0x0000ff00; + } + if (ram->diff.rammap_11_0b_0800) { + data |= cfg->bios.rammap_11_0b_0800 << 7; + mask |= 0x00000080; + } + if (ram->diff.rammap_11_0b_0400) { + data |= cfg->bios.rammap_11_0b_0400 << 5; + mask |= 0x00000020; + } + nv_mask(pfb, 0x10f444, mask, data); +} + +static int +gk104_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + struct nvkm_ram_data *next = ram->base.next; + + if (!nvkm_boolopt(device->cfgopt, "NvMemExec", true)) { + ram_exec(fuc, false); + return (ram->base.next == &ram->base.xition); + } + + gk104_ram_prog_0(pfb, 1000); + ram_exec(fuc, true); + gk104_ram_prog_0(pfb, next->freq); + + return (ram->base.next == &ram->base.xition); +} + +static void +gk104_ram_tidy(struct nvkm_fb *pfb) +{ + struct gk104_ram *ram = (void *)pfb->ram; + struct gk104_ramfuc *fuc = &ram->fuc; + ram->base.next = NULL; + ram_exec(fuc, false); +} + +struct gk104_ram_train { + u16 mask; + struct nvbios_M0209S remap; + struct nvbios_M0209S type00; + struct nvbios_M0209S type01; + struct nvbios_M0209S type04; + struct nvbios_M0209S type06; + struct nvbios_M0209S type07; + struct nvbios_M0209S type08; + struct nvbios_M0209S type09; +}; + +static int +gk104_ram_train_type(struct nvkm_fb *pfb, int i, u8 ramcfg, + struct gk104_ram_train *train) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvbios_M0205E M0205E; + struct nvbios_M0205S M0205S; + struct nvbios_M0209E M0209E; + struct nvbios_M0209S *remap = &train->remap; + struct nvbios_M0209S *value; + u8 ver, hdr, cnt, len; + u32 data; + + /* determine type of data for this index */ + if (!(data = nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E))) + return -ENOENT; + + switch (M0205E.type) { + case 0x00: value = &train->type00; break; + case 0x01: value = &train->type01; break; + case 0x04: value = &train->type04; break; + case 0x06: value = &train->type06; break; + case 0x07: value = &train->type07; break; + case 0x08: value = &train->type08; break; + case 0x09: value = &train->type09; break; + default: + return 0; + } + + /* training data index determined by ramcfg strap */ + if (!(data = nvbios_M0205Sp(bios, i, ramcfg, &ver, &hdr, &M0205S))) + return -EINVAL; + i = M0205S.data; + + /* training data format information */ + if (!(data = nvbios_M0209Ep(bios, i, &ver, &hdr, &cnt, &len, &M0209E))) + return -EINVAL; + + /* ... and the raw data */ + if (!(data = nvbios_M0209Sp(bios, i, 0, &ver, &hdr, value))) + return -EINVAL; + + if (M0209E.v02_07 == 2) { + /* of course! why wouldn't we have a pointer to another entry + * in the same table, and use the first one as an array of + * remap indices... + */ + if (!(data = nvbios_M0209Sp(bios, M0209E.v03, 0, &ver, &hdr, + remap))) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(value->data); i++) + value->data[i] = remap->data[value->data[i]]; + } else + if (M0209E.v02_07 != 1) + return -EINVAL; + + train->mask |= 1 << M0205E.type; + return 0; +} + +static int +gk104_ram_train_init_0(struct nvkm_fb *pfb, struct gk104_ram_train *train) +{ + int i, j; + + if ((train->mask & 0x03d3) != 0x03d3) { + nv_warn(pfb, "missing link training data\n"); + return -EINVAL; + } + + for (i = 0; i < 0x30; i++) { + for (j = 0; j < 8; j += 4) { + nv_wr32(pfb, 0x10f968 + j, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f920 + j, 0x00000000 | + train->type08.data[i] << 4 | + train->type06.data[i]); + nv_wr32(pfb, 0x10f918 + j, train->type00.data[i]); + nv_wr32(pfb, 0x10f920 + j, 0x00000100 | + train->type09.data[i] << 4 | + train->type07.data[i]); + nv_wr32(pfb, 0x10f918 + j, train->type01.data[i]); + } + } + + for (j = 0; j < 8; j += 4) { + for (i = 0; i < 0x100; i++) { + nv_wr32(pfb, 0x10f968 + j, i); + nv_wr32(pfb, 0x10f900 + j, train->type04.data[i]); + } + } + + return 0; +} + +static int +gk104_ram_train_init(struct nvkm_fb *pfb) +{ + u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb)); + struct gk104_ram_train *train; + int ret = -ENOMEM, i; + + if ((train = kzalloc(sizeof(*train), GFP_KERNEL))) { + for (i = 0; i < 0x100; i++) { + ret = gk104_ram_train_type(pfb, i, ramcfg, train); + if (ret && ret != -ENOENT) + break; + } + } + + switch (pfb->ram->type) { + case NV_MEM_TYPE_GDDR5: + ret = gk104_ram_train_init_0(pfb, train); + break; + default: + ret = 0; + break; + } + + kfree(train); + return ret; +} + +int +gk104_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object->parent; + struct gk104_ram *ram = (void *)object; + struct nvkm_bios *bios = nvkm_bios(pfb); + u8 ver, hdr, cnt, len, snr, ssz; + u32 data, save; + int ret, i; + + ret = nvkm_ram_init(&ram->base); + if (ret) + return ret; + + /* run a bunch of tables from rammap table. there's actually + * individual pointers for each rammap entry too, but, nvidia + * seem to just run the last two entries' scripts early on in + * their init, and never again.. we'll just run 'em all once + * for now. + * + * i strongly suspect that each script is for a separate mode + * (likely selected by 0x10f65c's lower bits?), and the + * binary driver skips the one that's already been setup by + * the init tables. + */ + data = nvbios_rammapTe(bios, &ver, &hdr, &cnt, &len, &snr, &ssz); + if (!data || hdr < 0x15) + return -EINVAL; + + cnt = nv_ro08(bios, data + 0x14); /* guess at count */ + data = nv_ro32(bios, data + 0x10); /* guess u32... */ + save = nv_rd32(pfb, 0x10f65c) & 0x000000f0; + for (i = 0; i < cnt; i++, data += 4) { + if (i != save >> 4) { + nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4); + nvbios_exec(&(struct nvbios_init) { + .subdev = nv_subdev(pfb), + .bios = bios, + .offset = nv_ro32(bios, data), + .execute = 1, + }); + } + } + nv_mask(pfb, 0x10f65c, 0x000000f0, save); + nv_mask(pfb, 0x10f584, 0x11000000, 0x00000000); + nv_wr32(pfb, 0x10ecc0, 0xffffffff); + nv_mask(pfb, 0x10f160, 0x00000010, 0x00000010); + + return gk104_ram_train_init(pfb); +} + +static int +gk104_ram_ctor_data(struct gk104_ram *ram, u8 ramcfg, int i) +{ + struct nvkm_fb *pfb = (void *)nv_object(ram)->parent; + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvkm_ram_data *cfg; + struct nvbios_ramcfg *d = &ram->diff; + struct nvbios_ramcfg *p, *n; + u8 ver, hdr, cnt, len; + u32 data; + int ret; + + if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL))) + return -ENOMEM; + p = &list_last_entry(&ram->cfg, typeof(*cfg), head)->bios; + n = &cfg->bios; + + /* memory config data for a range of target frequencies */ + data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios); + if (ret = -ENOENT, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12) + goto done; + + /* ... and a portion specific to the attached memory */ + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg, + &ver, &hdr, &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a) + goto done; + + /* lookup memory timings, if bios says they're present */ + if (cfg->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33) + goto done; + } + + list_add_tail(&cfg->head, &ram->cfg); + if (ret = 0, i == 0) + goto done; + + d->rammap_11_0a_03fe |= p->rammap_11_0a_03fe != n->rammap_11_0a_03fe; + d->rammap_11_09_01ff |= p->rammap_11_09_01ff != n->rammap_11_09_01ff; + d->rammap_11_0a_0400 |= p->rammap_11_0a_0400 != n->rammap_11_0a_0400; + d->rammap_11_0a_0800 |= p->rammap_11_0a_0800 != n->rammap_11_0a_0800; + d->rammap_11_0b_01f0 |= p->rammap_11_0b_01f0 != n->rammap_11_0b_01f0; + d->rammap_11_0b_0200 |= p->rammap_11_0b_0200 != n->rammap_11_0b_0200; + d->rammap_11_0d |= p->rammap_11_0d != n->rammap_11_0d; + d->rammap_11_0f |= p->rammap_11_0f != n->rammap_11_0f; + d->rammap_11_0e |= p->rammap_11_0e != n->rammap_11_0e; + d->rammap_11_0b_0800 |= p->rammap_11_0b_0800 != n->rammap_11_0b_0800; + d->rammap_11_0b_0400 |= p->rammap_11_0b_0400 != n->rammap_11_0b_0400; + d->ramcfg_11_01_01 |= p->ramcfg_11_01_01 != n->ramcfg_11_01_01; + d->ramcfg_11_01_02 |= p->ramcfg_11_01_02 != n->ramcfg_11_01_02; + d->ramcfg_11_01_10 |= p->ramcfg_11_01_10 != n->ramcfg_11_01_10; + d->ramcfg_11_02_03 |= p->ramcfg_11_02_03 != n->ramcfg_11_02_03; + d->ramcfg_11_08_20 |= p->ramcfg_11_08_20 != n->ramcfg_11_08_20; + d->timing_20_30_07 |= p->timing_20_30_07 != n->timing_20_30_07; +done: + if (ret) + kfree(cfg); + return ret; +} + +static void +gk104_ram_dtor(struct nvkm_object *object) +{ + struct gk104_ram *ram = (void *)object; + struct nvkm_ram_data *cfg, *tmp; + + list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) { + kfree(cfg); + } + + nvkm_ram_destroy(&ram->base); +} + +static int +gk104_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nvkm_gpio *gpio = nvkm_gpio(pfb); + struct dcb_gpio_func func; + struct gk104_ram *ram; + int ret, i; + u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb)); + u32 tmp; + + ret = gf100_ram_create(parent, engine, oclass, 0x022554, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + INIT_LIST_HEAD(&ram->cfg); + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + case NV_MEM_TYPE_GDDR5: + ram->base.calc = gk104_ram_calc; + ram->base.prog = gk104_ram_prog; + ram->base.tidy = gk104_ram_tidy; + break; + default: + nv_warn(pfb, "reclocking of this RAM type is unsupported\n"); + break; + } + + /* calculate a mask of differently configured memory partitions, + * because, of course reclocking wasn't complicated enough + * already without having to treat some of them differently to + * the others.... + */ + ram->parts = nv_rd32(pfb, 0x022438); + ram->pmask = nv_rd32(pfb, 0x022554); + ram->pnuts = 0; + for (i = 0, tmp = 0; i < ram->parts; i++) { + if (!(ram->pmask & (1 << i))) { + u32 cfg1 = nv_rd32(pfb, 0x110204 + (i * 0x1000)); + if (tmp && tmp != cfg1) { + ram->pnuts |= (1 << i); + continue; + } + tmp = cfg1; + } + } + + /* parse bios data for all rammap table entries up-front, and + * build information on whether certain fields differ between + * any of the entries. + * + * the binary driver appears to completely ignore some fields + * when all entries contain the same value. at first, it was + * hoped that these were mere optimisations and the bios init + * tables had configured as per the values here, but there is + * evidence now to suggest that this isn't the case and we do + * need to treat this condition as a "don't touch" indicator. + */ + for (i = 0; !ret; i++) { + ret = gk104_ram_ctor_data(ram, ramcfg, i); + if (ret && ret != -ENOENT) { + nv_error(pfb, "failed to parse ramcfg data\n"); + return ret; + } + } + + /* parse bios data for both pll's */ + ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll); + if (ret) { + nv_error(pfb, "mclk refpll data not found\n"); + return ret; + } + + ret = nvbios_pll_parse(bios, 0x04, &ram->fuc.mempll); + if (ret) { + nv_error(pfb, "mclk pll data not found\n"); + return ret; + } + + /* lookup memory voltage gpios */ + ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func); + if (ret == 0) { + ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04)); + ram->fuc.r_funcMV[0] = (func.log[0] ^ 2) << 12; + ram->fuc.r_funcMV[1] = (func.log[1] ^ 2) << 12; + } + + ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func); + if (ret == 0) { + ram->fuc.r_gpio2E = ramfuc_reg(0x00d610 + (func.line * 0x04)); + ram->fuc.r_func2E[0] = (func.log[0] ^ 2) << 12; + ram->fuc.r_func2E[1] = (func.log[1] ^ 2) << 12; + } + + ram->fuc.r_gpiotrig = ramfuc_reg(0x00d604); + + ram->fuc.r_0x132020 = ramfuc_reg(0x132020); + ram->fuc.r_0x132028 = ramfuc_reg(0x132028); + ram->fuc.r_0x132024 = ramfuc_reg(0x132024); + ram->fuc.r_0x132030 = ramfuc_reg(0x132030); + ram->fuc.r_0x132034 = ramfuc_reg(0x132034); + ram->fuc.r_0x132000 = ramfuc_reg(0x132000); + ram->fuc.r_0x132004 = ramfuc_reg(0x132004); + ram->fuc.r_0x132040 = ramfuc_reg(0x132040); + + ram->fuc.r_0x10f248 = ramfuc_reg(0x10f248); + ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290); + ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294); + ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298); + ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c); + ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0); + ram->fuc.r_0x10f2a4 = ramfuc_reg(0x10f2a4); + ram->fuc.r_0x10f2a8 = ramfuc_reg(0x10f2a8); + ram->fuc.r_0x10f2ac = ramfuc_reg(0x10f2ac); + ram->fuc.r_0x10f2cc = ramfuc_reg(0x10f2cc); + ram->fuc.r_0x10f2e8 = ramfuc_reg(0x10f2e8); + ram->fuc.r_0x10f250 = ramfuc_reg(0x10f250); + ram->fuc.r_0x10f24c = ramfuc_reg(0x10f24c); + ram->fuc.r_0x10fec4 = ramfuc_reg(0x10fec4); + ram->fuc.r_0x10fec8 = ramfuc_reg(0x10fec8); + ram->fuc.r_0x10f604 = ramfuc_reg(0x10f604); + ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614); + ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610); + ram->fuc.r_0x100770 = ramfuc_reg(0x100770); + ram->fuc.r_0x100778 = ramfuc_reg(0x100778); + ram->fuc.r_0x10f224 = ramfuc_reg(0x10f224); + + ram->fuc.r_0x10f870 = ramfuc_reg(0x10f870); + ram->fuc.r_0x10f698 = ramfuc_reg(0x10f698); + ram->fuc.r_0x10f694 = ramfuc_reg(0x10f694); + ram->fuc.r_0x10f6b8 = ramfuc_reg(0x10f6b8); + ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808); + ram->fuc.r_0x10f670 = ramfuc_reg(0x10f670); + ram->fuc.r_0x10f60c = ramfuc_reg(0x10f60c); + ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830); + ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec); + ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800); + ram->fuc.r_0x10f82c = ramfuc_reg(0x10f82c); + + ram->fuc.r_0x10f978 = ramfuc_reg(0x10f978); + ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910); + ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914); + + switch (ram->base.type) { + case NV_MEM_TYPE_GDDR5: + ram->fuc.r_mr[0] = ramfuc_reg(0x10f300); + ram->fuc.r_mr[1] = ramfuc_reg(0x10f330); + ram->fuc.r_mr[2] = ramfuc_reg(0x10f334); + ram->fuc.r_mr[3] = ramfuc_reg(0x10f338); + ram->fuc.r_mr[4] = ramfuc_reg(0x10f33c); + ram->fuc.r_mr[5] = ramfuc_reg(0x10f340); + ram->fuc.r_mr[6] = ramfuc_reg(0x10f344); + ram->fuc.r_mr[7] = ramfuc_reg(0x10f348); + ram->fuc.r_mr[8] = ramfuc_reg(0x10f354); + ram->fuc.r_mr[15] = ramfuc_reg(0x10f34c); + break; + case NV_MEM_TYPE_DDR3: + ram->fuc.r_mr[0] = ramfuc_reg(0x10f300); + ram->fuc.r_mr[2] = ramfuc_reg(0x10f320); + break; + default: + break; + } + + ram->fuc.r_0x62c000 = ramfuc_reg(0x62c000); + ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200); + ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210); + ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310); + ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314); + ram->fuc.r_0x10f318 = ramfuc_reg(0x10f318); + ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090); + ram->fuc.r_0x10f69c = ramfuc_reg(0x10f69c); + ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824); + ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0); + ram->fuc.r_0x1373f4 = ramfuc_reg(0x1373f4); + ram->fuc.r_0x137320 = ramfuc_reg(0x137320); + ram->fuc.r_0x10f65c = ramfuc_reg(0x10f65c); + ram->fuc.r_0x10f6bc = ramfuc_reg(0x10f6bc); + ram->fuc.r_0x100710 = ramfuc_reg(0x100710); + ram->fuc.r_0x100750 = ramfuc_reg(0x100750); + return 0; +} + +struct nvkm_oclass +gk104_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gk104_ram_ctor, + .dtor = gk104_ram_dtor, + .init = gk104_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c new file mode 100644 index 000000000..a298b39f5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c @@ -0,0 +1,55 @@ +/* + * Copyright 2013 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 "gf100.h" + +struct gm107_ram { + struct nvkm_ram base; +}; + +static int +gm107_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gm107_ram *ram; + int ret; + + ret = gf100_ram_create(parent, engine, oclass, 0x021c14, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + return 0; +} + +struct nvkm_oclass +gm107_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gm107_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = gk104_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c new file mode 100644 index 000000000..24176401b --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c @@ -0,0 +1,1012 @@ +/* + * Copyright 2013 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 + * Roy Spliet + */ + +#include "ramfuc.h" +#include "nv50.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* XXX: Remove when memx gains GPIO support */ +extern int nv50_gpio_location(int line, u32 *reg, u32 *shift); + +struct gt215_ramfuc { + struct ramfuc base; + struct ramfuc_reg r_0x001610; + struct ramfuc_reg r_0x001700; + struct ramfuc_reg r_0x002504; + struct ramfuc_reg r_0x004000; + struct ramfuc_reg r_0x004004; + struct ramfuc_reg r_0x004018; + struct ramfuc_reg r_0x004128; + struct ramfuc_reg r_0x004168; + struct ramfuc_reg r_0x100080; + struct ramfuc_reg r_0x100200; + struct ramfuc_reg r_0x100210; + struct ramfuc_reg r_0x100220[9]; + struct ramfuc_reg r_0x100264; + struct ramfuc_reg r_0x1002d0; + struct ramfuc_reg r_0x1002d4; + struct ramfuc_reg r_0x1002dc; + struct ramfuc_reg r_0x10053c; + struct ramfuc_reg r_0x1005a0; + struct ramfuc_reg r_0x1005a4; + struct ramfuc_reg r_0x100700; + struct ramfuc_reg r_0x100714; + struct ramfuc_reg r_0x100718; + struct ramfuc_reg r_0x10071c; + struct ramfuc_reg r_0x100720; + struct ramfuc_reg r_0x100760; + struct ramfuc_reg r_0x1007a0; + struct ramfuc_reg r_0x1007e0; + struct ramfuc_reg r_0x100da0; + struct ramfuc_reg r_0x10f804; + struct ramfuc_reg r_0x1110e0; + struct ramfuc_reg r_0x111100; + struct ramfuc_reg r_0x111104; + struct ramfuc_reg r_0x1111e0; + struct ramfuc_reg r_0x111400; + struct ramfuc_reg r_0x611200; + struct ramfuc_reg r_mr[4]; + struct ramfuc_reg r_gpioFBVREF; +}; + +struct gt215_ltrain { + enum { + NVA3_TRAIN_UNKNOWN, + NVA3_TRAIN_UNSUPPORTED, + NVA3_TRAIN_ONCE, + NVA3_TRAIN_EXEC, + NVA3_TRAIN_DONE + } state; + u32 r_100720; + u32 r_1111e0; + u32 r_111400; + struct nvkm_mem *mem; +}; + +struct gt215_ram { + struct nvkm_ram base; + struct gt215_ramfuc fuc; + struct gt215_ltrain ltrain; +}; + +void +gt215_link_train_calc(u32 *vals, struct gt215_ltrain *train) +{ + int i, lo, hi; + u8 median[8], bins[4] = {0, 0, 0, 0}, bin = 0, qty = 0; + + for (i = 0; i < 8; i++) { + for (lo = 0; lo < 0x40; lo++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (vals[lo] & (0x101 << i)) + break; + } + + if (lo == 0x40) + return; + + for (hi = lo + 1; hi < 0x40; hi++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (!(vals[hi] & (0x101 << i))) { + hi--; + break; + } + } + + median[i] = ((hi - lo) >> 1) + lo; + bins[(median[i] & 0xf0) >> 4]++; + median[i] += 0x30; + } + + /* Find the best value for 0x1111e0 */ + for (i = 0; i < 4; i++) { + if (bins[i] > qty) { + bin = i + 3; + qty = bins[i]; + } + } + + train->r_100720 = 0; + for (i = 0; i < 8; i++) { + median[i] = max(median[i], (u8) (bin << 4)); + median[i] = min(median[i], (u8) ((bin << 4) | 0xf)); + + train->r_100720 |= ((median[i] & 0x0f) << (i << 2)); + } + + train->r_1111e0 = 0x02000000 | (bin * 0x101); + train->r_111400 = 0x0; +} + +/* + * Link training for (at least) DDR3 + */ +int +gt215_link_train(struct nvkm_fb *pfb) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct nvkm_clk *clk = nvkm_clk(pfb); + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_device *device = nv_device(pfb); + struct gt215_ramfuc *fuc = &ram->fuc; + u32 *result, r1700; + int ret, i; + struct nvbios_M0205T M0205T = { 0 }; + u8 ver, hdr, cnt, len, snr, ssz; + unsigned int clk_current; + unsigned long flags; + unsigned long *f = &flags; + + if (nvkm_boolopt(device->cfgopt, "NvMemExec", true) != true) + return -ENOSYS; + + /* XXX: Multiple partitions? */ + result = kmalloc(64 * sizeof(u32), GFP_KERNEL); + if (!result) + return -ENOMEM; + + train->state = NVA3_TRAIN_EXEC; + + /* Clock speeds for training and back */ + nvbios_M0205Tp(bios, &ver, &hdr, &cnt, &len, &snr, &ssz, &M0205T); + if (M0205T.freq == 0) + return -ENOENT; + + clk_current = clk->read(clk, nv_clk_src_mem); + + ret = gt215_clk_pre(clk, f); + if (ret) + goto out; + + /* First: clock up/down */ + ret = ram->base.calc(pfb, (u32) M0205T.freq * 1000); + if (ret) + goto out; + + /* Do this *after* calc, eliminates write in script */ + nv_wr32(pfb, 0x111400, 0x00000000); + /* XXX: Magic writes that improve train reliability? */ + nv_mask(pfb, 0x100674, 0x0000ffff, 0x00000000); + nv_mask(pfb, 0x1005e4, 0x0000ffff, 0x00000000); + nv_mask(pfb, 0x100b0c, 0x000000ff, 0x00000000); + nv_wr32(pfb, 0x100c04, 0x00000400); + + /* Now the training script */ + r1700 = ram_rd32(fuc, 0x001700); + + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x611200, 0x3300); + ram_wait_vblank(fuc); + ram_wait(fuc, 0x611200, 0x00000003, 0x00000000, 500000); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000003); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_wr32(fuc, 0x001700, 0x00000000); + + ram_train(fuc); + + /* Reset */ + ram_mask(fuc, 0x10f804, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10053c, 0x0); + ram_wr32(fuc, 0x100720, train->r_100720); + ram_wr32(fuc, 0x1111e0, train->r_1111e0); + ram_wr32(fuc, 0x111400, train->r_111400); + ram_nuke(fuc, 0x100080); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000020); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x001700, r1700); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000080); + ram_wr32(fuc, 0x611200, 0x3330); + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + + ram_exec(fuc, true); + + ram->base.calc(pfb, clk_current); + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nv_mask(pfb, 0x616308, 0x10, 0x10); + nv_mask(pfb, 0x616b08, 0x10, 0x10); + + gt215_clk_post(clk, f); + + ram_train_result(pfb, result, 64); + for (i = 0; i < 64; i++) + nv_debug(pfb, "Train: %08x", result[i]); + gt215_link_train_calc(result, train); + + nv_debug(pfb, "Train: %08x %08x %08x", train->r_100720, + train->r_1111e0, train->r_111400); + + kfree(result); + + train->state = NVA3_TRAIN_DONE; + + return ret; + +out: + if(ret == -EBUSY) + f = NULL; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + gt215_clk_post(clk, f); + return ret; +} + +int +gt215_link_train_init(struct nvkm_fb *pfb) +{ + static const u32 pattern[16] = { + 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee, + 0x00000000, 0x11111111, 0x44444444, 0xdddddddd, + 0x33333333, 0x55555555, 0x77777777, 0x66666666, + 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb, + }; + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_mem *mem; + struct nvbios_M0205E M0205E; + u8 ver, hdr, cnt, len; + u32 r001700; + int ret, i = 0; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + /* We support type "5" + * XXX: training pattern table appears to be unused for this routine */ + if (!nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)) + return -ENOENT; + + if (M0205E.type != 5) + return 0; + + train->state = NVA3_TRAIN_ONCE; + + ret = pfb->ram->get(pfb, 0x8000, 0x10000, 0, 0x800, &ram->ltrain.mem); + if (ret) + return ret; + + mem = ram->ltrain.mem; + + nv_wr32(pfb, 0x100538, 0x10000000 | (mem->offset >> 16)); + nv_wr32(pfb, 0x1005a8, 0x0000ffff); + nv_mask(pfb, 0x10f800, 0x00000001, 0x00000001); + + for (i = 0; i < 0x30; i++) { + nv_wr32(pfb, 0x10f8c0, (i << 8) | i); + nv_wr32(pfb, 0x10f900, pattern[i % 16]); + } + + for (i = 0; i < 0x30; i++) { + nv_wr32(pfb, 0x10f8e0, (i << 8) | i); + nv_wr32(pfb, 0x10f920, pattern[i % 16]); + } + + /* And upload the pattern */ + r001700 = nv_rd32(pfb, 0x1700); + nv_wr32(pfb, 0x1700, mem->offset >> 16); + for (i = 0; i < 16; i++) + nv_wr32(pfb, 0x700000 + (i << 2), pattern[i]); + for (i = 0; i < 16; i++) + nv_wr32(pfb, 0x700100 + (i << 2), pattern[i]); + nv_wr32(pfb, 0x1700, r001700); + + train->r_100720 = nv_rd32(pfb, 0x100720); + train->r_1111e0 = nv_rd32(pfb, 0x1111e0); + train->r_111400 = nv_rd32(pfb, 0x111400); + return 0; +} + +void +gt215_link_train_fini(struct nvkm_fb *pfb) +{ + struct gt215_ram *ram = (void *)pfb->ram; + + if (ram->ltrain.mem) + pfb->ram->put(pfb, &ram->ltrain.mem); +} + +/* + * RAM reclocking + */ +#define T(t) cfg->timing_10_##t +static int +gt215_ram_timing_calc(struct nvkm_fb *pfb, u32 *timing) +{ + struct gt215_ram *ram = (void *)pfb->ram; + struct nvbios_ramcfg *cfg = &ram->base.target.bios; + int tUNK_base, tUNK_40_0, prevCL; + u32 cur2, cur3, cur7, cur8; + + cur2 = nv_rd32(pfb, 0x100228); + cur3 = nv_rd32(pfb, 0x10022c); + cur7 = nv_rd32(pfb, 0x10023c); + cur8 = nv_rd32(pfb, 0x100240); + + + switch ((!T(CWL)) * ram->base.type) { + case NV_MEM_TYPE_DDR2: + T(CWL) = T(CL) - 1; + break; + case NV_MEM_TYPE_GDDR3: + T(CWL) = ((cur2 & 0xff000000) >> 24) + 1; + break; + } + + prevCL = (cur3 & 0x000000ff) + 1; + tUNK_base = ((cur7 & 0x00ff0000) >> 16) - prevCL; + + timing[0] = (T(RP) << 24 | T(RAS) << 16 | T(RFC) << 8 | T(RC)); + timing[1] = (T(WR) + 1 + T(CWL)) << 24 | + max_t(u8,T(18), 1) << 16 | + (T(WTR) + 1 + T(CWL)) << 8 | + (5 + T(CL) - T(CWL)); + timing[2] = (T(CWL) - 1) << 24 | + (T(RRD) << 16) | + (T(RCDWR) << 8) | + T(RCDRD); + timing[3] = (cur3 & 0x00ff0000) | + (0x30 + T(CL)) << 24 | + (0xb + T(CL)) << 8 | + (T(CL) - 1); + timing[4] = T(20) << 24 | + T(21) << 16 | + T(13) << 8 | + T(13); + timing[5] = T(RFC) << 24 | + max_t(u8,T(RCDRD), T(RCDWR)) << 16 | + max_t(u8, (T(CWL) + 6), (T(CL) + 2)) << 8 | + T(RP); + timing[6] = (0x5a + T(CL)) << 16 | + max_t(u8, 1, (6 - T(CL) + T(CWL))) << 8 | + (0x50 + T(CL) - T(CWL)); + timing[7] = (cur7 & 0xff000000) | + ((tUNK_base + T(CL)) << 16) | + 0x202; + timing[8] = cur8 & 0xffffff00; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + case NV_MEM_TYPE_GDDR3: + tUNK_40_0 = prevCL - (cur8 & 0xff); + if (tUNK_40_0 > 0) + timing[8] |= T(CL); + break; + default: + break; + } + + nv_debug(pfb, "Entry: 220: %08x %08x %08x %08x\n", + timing[0], timing[1], timing[2], timing[3]); + nv_debug(pfb, " 230: %08x %08x %08x %08x\n", + timing[4], timing[5], timing[6], timing[7]); + nv_debug(pfb, " 240: %08x\n", timing[8]); + return 0; +} +#undef T + +static void +nvkm_sddr2_dll_reset(struct gt215_ramfuc *fuc) +{ + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[0], 0x100, 0x000); + ram_nsec(fuc, 1000); +} + +static void +nvkm_sddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x1)) { + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +nvkm_gddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x40)) { + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +gt215_ram_lock_pll(struct gt215_ramfuc *fuc, struct gt215_clk_info *mclk) +{ + ram_wr32(fuc, 0x004004, mclk->pll); + ram_mask(fuc, 0x004000, 0x00000001, 0x00000001); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000000); + ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000010); +} + +static void +gt215_ram_fbvref(struct gt215_ramfuc *fuc, u32 val) +{ + struct nvkm_gpio *gpio = nvkm_gpio(fuc->base.pfb); + struct dcb_gpio_func func; + u32 reg, sh, gpio_val; + int ret; + + if (gpio->get(gpio, 0, 0x2e, DCB_GPIO_UNUSED) != val) { + ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func); + if (ret) + return; + + nv50_gpio_location(func.line, ®, &sh); + gpio_val = ram_rd32(fuc, gpioFBVREF); + if (gpio_val & (8 << sh)) + val = !val; + + ram_mask(fuc, gpioFBVREF, (0x3 << sh), ((val | 0x2) << sh)); + ram_nsec(fuc, 20000); + } +} + +static int +gt215_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ramfuc *fuc = &ram->fuc; + struct gt215_ltrain *train = &ram->ltrain; + struct gt215_clk_info mclk; + struct nvkm_ram_data *next; + u8 ver, hdr, cnt, len, strap; + u32 data; + u32 r004018, r100760, r100da0, r111100, ctrl; + u32 unk714, unk718, unk71c; + int ret, i; + u32 timing[9]; + bool pll2pll; + + next = &ram->base.target; + next->freq = freq; + ram->base.next = next; + + if (ram->ltrain.state == NVA3_TRAIN_ONCE) + gt215_link_train(pfb); + + /* lookup memory config data relevant to the target frequency */ + i = 0; + data = nvbios_rammapEm(bios, freq / 1000, &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x05) { + nv_error(pfb, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(nv_subdev(pfb)); + if (strap >= cnt) { + nv_error(pfb, "invalid ramcfg strap\n"); + return -EINVAL; + } + + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap, + &ver, &hdr, &next->bios); + if (!data || ver != 0x10 || hdr < 0x09) { + nv_error(pfb, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + if (next->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, next->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x17) { + nv_error(pfb, "invalid/missing timing entry\n"); + return -EINVAL; + } + } + + ret = gt215_pll_info(nvkm_clk(pfb), 0x12, 0x4000, freq, &mclk); + if (ret < 0) { + nv_error(pfb, "failed mclk calculation\n"); + return ret; + } + + gt215_ram_timing_calc(pfb, timing); + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + /* Determine ram-specific MR values */ + ram->base.mr[0] = ram_rd32(fuc, mr[0]); + ram->base.mr[1] = ram_rd32(fuc, mr[1]); + ram->base.mr[2] = ram_rd32(fuc, mr[2]); + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + ret = nvkm_sddr2_calc(&ram->base); + break; + case NV_MEM_TYPE_DDR3: + ret = nvkm_sddr3_calc(&ram->base); + break; + case NV_MEM_TYPE_GDDR3: + ret = nvkm_gddr3_calc(&ram->base); + break; + default: + ret = -ENOSYS; + break; + } + + if (ret) + return ret; + + /* XXX: where the fuck does 750MHz come from? */ + if (freq <= 750000) { + r004018 = 0x10000000; + r100760 = 0x22222222; + r100da0 = 0x00000010; + } else { + r004018 = 0x00000000; + r100760 = 0x00000000; + r100da0 = 0x00000000; + } + + if (!next->bios.ramcfg_10_DLLoff) + r004018 |= 0x00004000; + + /* pll2pll requires to switch to a safe clock first */ + ctrl = ram_rd32(fuc, 0x004000); + pll2pll = (!(ctrl & 0x00000008)) && mclk.pll; + + /* Pre, NVIDIA does this outside the script */ + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); + } else { + ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); + ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); + } + /* Always disable this bit during reclock */ + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + + /* If switching from non-pll to pll, lock before disabling FB */ + if (mclk.pll && !pll2pll) { + ram_mask(fuc, 0x004128, 0x003f3141, mclk.clk | 0x00000101); + gt215_ram_lock_pll(fuc, &mclk); + } + + /* Start with disabling some CRTCs and PFIFO? */ + ram_wait_vblank(fuc); + ram_wr32(fuc, 0x611200, 0x3300); + ram_mask(fuc, 0x002504, 0x1, 0x1); + ram_nsec(fuc, 10000); + ram_wait(fuc, 0x002504, 0x10, 0x10, 20000); /* XXX: or longer? */ + ram_block(fuc); + ram_nsec(fuc, 2000); + + if (!next->bios.ramcfg_10_02_10) { + if (ram->base.type == NV_MEM_TYPE_GDDR3) + ram_mask(fuc, 0x111100, 0x04020000, 0x00020000); + else + ram_mask(fuc, 0x111100, 0x04020000, 0x04020000); + } + + /* If we're disabling the DLL, do it now */ + switch (next->bios.ramcfg_10_DLLoff * ram->base.type) { + case NV_MEM_TYPE_DDR3: + nvkm_sddr3_dll_disable(fuc, ram->base.mr); + break; + case NV_MEM_TYPE_GDDR3: + nvkm_gddr3_dll_disable(fuc, ram->base.mr); + break; + } + + if (fuc->r_gpioFBVREF.addr && next->bios.timing_10_ODT) + gt215_ram_fbvref(fuc, 0); + + /* Brace RAM for impact */ + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x100210, 0x00000000); + ram_wr32(fuc, 0x1002dc, 0x00000001); + ram_nsec(fuc, 2000); + + if (nv_device(pfb)->chipset == 0xa3 && freq <= 500000) + ram_mask(fuc, 0x100700, 0x00000006, 0x00000006); + + /* Fiddle with clocks */ + /* There's 4 scenario's + * pll->pll: first switch to a 324MHz clock, set up new PLL, switch + * clk->pll: Set up new PLL, switch + * pll->clk: Set up clock, switch + * clk->clk: Overwrite ctrl and other bits, switch */ + + /* Switch to regular clock - 324MHz */ + if (pll2pll) { + ram_mask(fuc, 0x004000, 0x00000004, 0x00000004); + ram_mask(fuc, 0x004168, 0x003f3141, 0x00083101); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00001000); + gt215_ram_lock_pll(fuc, &mclk); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004000, 0x00000105, 0x00000105); + ram_wr32(fuc, 0x004018, 0x00001000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } else { + ram_mask(fuc, 0x004168, 0x003f3141, mclk.clk | 0x00000101); + ram_mask(fuc, 0x004000, 0x00000108, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00009000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } + ram_nsec(fuc, 20000); + + if (next->bios.rammap_10_04_08) { + ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 | + next->bios.ramcfg_10_05 << 8 | + next->bios.ramcfg_10_05); + ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 | + next->bios.ramcfg_10_07); + ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 | + next->bios.ramcfg_10_03_0f << 16 | + next->bios.ramcfg_10_09_0f | + 0x80000000); + ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); + } else { + if (train->state == NVA3_TRAIN_DONE) { + ram_wr32(fuc, 0x100080, 0x1020); + ram_mask(fuc, 0x111400, 0xffffffff, train->r_111400); + ram_mask(fuc, 0x1111e0, 0xffffffff, train->r_1111e0); + ram_mask(fuc, 0x100720, 0xffffffff, train->r_100720); + } + ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_mask(fuc, 0x100760, 0x22222222, r100760); + ram_mask(fuc, 0x1007a0, 0x22222222, r100760); + ram_mask(fuc, 0x1007e0, 0x22222222, r100760); + } + + if (nv_device(pfb)->chipset == 0xa3 && freq > 500000) { + ram_mask(fuc, 0x100700, 0x00000006, 0x00000000); + } + + /* Final switch */ + if (mclk.pll) { + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000000); + } + + ram_wr32(fuc, 0x1002dc, 0x00000000); + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x100210, 0x80000000); + ram_nsec(fuc, 2000); + + /* Set RAM MR parameters and timings */ + for (i = 2; i >= 0; i--) { + if (ram_rd32(fuc, mr[i]) != ram->base.mr[i]) { + ram_wr32(fuc, mr[i], ram->base.mr[i]); + ram_nsec(fuc, 1000); + } + } + + ram_wr32(fuc, 0x100220[3], timing[3]); + ram_wr32(fuc, 0x100220[1], timing[1]); + ram_wr32(fuc, 0x100220[6], timing[6]); + ram_wr32(fuc, 0x100220[7], timing[7]); + ram_wr32(fuc, 0x100220[2], timing[2]); + ram_wr32(fuc, 0x100220[4], timing[4]); + ram_wr32(fuc, 0x100220[5], timing[5]); + ram_wr32(fuc, 0x100220[0], timing[0]); + ram_wr32(fuc, 0x100220[8], timing[8]); + + /* Misc */ + ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12); + + /* XXX: A lot of "chipset"/"ram type" specific stuff...? */ + unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000130; + unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; + unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; + r111100 = ram_rd32(fuc, 0x111100) & ~0x3a800000; + + if (next->bios.ramcfg_10_02_04) { + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + if (nv_device(pfb)->chipset != 0xa8) + r111100 |= 0x00000004; + /* no break */ + case NV_MEM_TYPE_DDR2: + r111100 |= 0x08000000; + break; + default: + break; + } + } else { + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + r111100 |= 0x1a800000; + unk714 |= 0x00000010; + break; + case NV_MEM_TYPE_DDR3: + if (nv_device(pfb)->chipset == 0xa8) { + r111100 |= 0x08000000; + } else { + r111100 &= ~0x00000004; + r111100 |= 0x12800000; + } + unk714 |= 0x00000010; + break; + case NV_MEM_TYPE_GDDR3: + r111100 |= 0x30000000; + unk714 |= 0x00000020; + break; + default: + break; + } + } + + unk714 |= (next->bios.ramcfg_10_04_01) << 8; + + if (next->bios.ramcfg_10_02_20) + unk714 |= 0xf0000000; + if (next->bios.ramcfg_10_02_02) + unk718 |= 0x00000100; + if (next->bios.ramcfg_10_02_01) + unk71c |= 0x00000100; + if (next->bios.timing_10_24 != 0xff) { + unk718 &= ~0xf0000000; + unk718 |= next->bios.timing_10_24 << 28; + } + if (next->bios.ramcfg_10_02_10) + r111100 &= ~0x04020000; + + ram_mask(fuc, 0x100714, 0xffffffff, unk714); + ram_mask(fuc, 0x10071c, 0xffffffff, unk71c); + ram_mask(fuc, 0x100718, 0xffffffff, unk718); + ram_mask(fuc, 0x111100, 0xffffffff, r111100); + + if (fuc->r_gpioFBVREF.addr && !next->bios.timing_10_ODT) + gt215_ram_fbvref(fuc, 1); + + /* Reset DLL */ + if (!next->bios.ramcfg_10_DLLoff) + nvkm_sddr2_dll_reset(fuc); + + if (ram->base.type == NV_MEM_TYPE_GDDR3) { + ram_nsec(fuc, 31000); + } else { + ram_nsec(fuc, 14000); + } + + if (ram->base.type == NV_MEM_TYPE_DDR3) { + ram_wr32(fuc, 0x100264, 0x1); + ram_nsec(fuc, 2000); + } + + ram_nuke(fuc, 0x100700); + ram_mask(fuc, 0x100700, 0x01000000, 0x01000000); + ram_mask(fuc, 0x100700, 0x01000000, 0x00000000); + + /* Re-enable FB */ + ram_unblock(fuc); + ram_wr32(fuc, 0x611200, 0x3330); + + /* Post fiddlings */ + if (next->bios.rammap_10_04_02) + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000180, 0x00000180); + ram_mask(fuc, 0x111100, 0x40000000, 0x00000000); + } else { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000600); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004168, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004168, 0x00000100, 0x00000000); + } else { + ram_mask(fuc, 0x004000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000100, 0x00000000); + } + + return 0; +} + +static int +gt215_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ramfuc *fuc = &ram->fuc; + bool exec = nvkm_boolopt(device->cfgopt, "NvMemExec", true); + + if (exec) { + nv_mask(pfb, 0x001534, 0x2, 0x2); + + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nv_mask(pfb, 0x002504, 0x1, 0x0); + nv_mask(pfb, 0x001534, 0x2, 0x0); + + nv_mask(pfb, 0x616308, 0x10, 0x10); + nv_mask(pfb, 0x616b08, 0x10, 0x10); + } else { + ram_exec(fuc, false); + } + return 0; +} + +static void +gt215_ram_tidy(struct nvkm_fb *pfb) +{ + struct gt215_ram *ram = (void *)pfb->ram; + struct gt215_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, false); +} + +static int +gt215_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = (void *)object->parent; + struct gt215_ram *ram = (void *)object; + int ret; + + ret = nvkm_ram_init(&ram->base); + if (ret) + return ret; + + gt215_link_train_init(pfb); + return 0; +} + +static int +gt215_ram_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_fb *pfb = (void *)object->parent; + + if (!suspend) + gt215_link_train_fini(pfb); + + return 0; +} + +static int +gt215_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 datasize, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_gpio *gpio = nvkm_gpio(pfb); + struct dcb_gpio_func func; + struct gt215_ram *ram; + int ret, i; + u32 reg, shift; + + ret = nv50_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + case NV_MEM_TYPE_DDR3: + case NV_MEM_TYPE_GDDR3: + ram->base.calc = gt215_ram_calc; + ram->base.prog = gt215_ram_prog; + ram->base.tidy = gt215_ram_tidy; + break; + default: + nv_warn(ram, "reclocking of this ram type unsupported\n"); + return 0; + } + + ram->fuc.r_0x001610 = ramfuc_reg(0x001610); + ram->fuc.r_0x001700 = ramfuc_reg(0x001700); + ram->fuc.r_0x002504 = ramfuc_reg(0x002504); + ram->fuc.r_0x004000 = ramfuc_reg(0x004000); + ram->fuc.r_0x004004 = ramfuc_reg(0x004004); + ram->fuc.r_0x004018 = ramfuc_reg(0x004018); + ram->fuc.r_0x004128 = ramfuc_reg(0x004128); + ram->fuc.r_0x004168 = ramfuc_reg(0x004168); + ram->fuc.r_0x100080 = ramfuc_reg(0x100080); + ram->fuc.r_0x100200 = ramfuc_reg(0x100200); + ram->fuc.r_0x100210 = ramfuc_reg(0x100210); + for (i = 0; i < 9; i++) + ram->fuc.r_0x100220[i] = ramfuc_reg(0x100220 + (i * 4)); + ram->fuc.r_0x100264 = ramfuc_reg(0x100264); + ram->fuc.r_0x1002d0 = ramfuc_reg(0x1002d0); + ram->fuc.r_0x1002d4 = ramfuc_reg(0x1002d4); + ram->fuc.r_0x1002dc = ramfuc_reg(0x1002dc); + ram->fuc.r_0x10053c = ramfuc_reg(0x10053c); + ram->fuc.r_0x1005a0 = ramfuc_reg(0x1005a0); + ram->fuc.r_0x1005a4 = ramfuc_reg(0x1005a4); + ram->fuc.r_0x100700 = ramfuc_reg(0x100700); + ram->fuc.r_0x100714 = ramfuc_reg(0x100714); + ram->fuc.r_0x100718 = ramfuc_reg(0x100718); + ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); + ram->fuc.r_0x100720 = ramfuc_reg(0x100720); + ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask); + ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask); + ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask); + ram->fuc.r_0x100da0 = ramfuc_stride(0x100da0, 4, ram->base.part_mask); + ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); + ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask); + ram->fuc.r_0x111100 = ramfuc_reg(0x111100); + ram->fuc.r_0x111104 = ramfuc_reg(0x111104); + ram->fuc.r_0x1111e0 = ramfuc_reg(0x1111e0); + ram->fuc.r_0x111400 = ramfuc_reg(0x111400); + ram->fuc.r_0x611200 = ramfuc_reg(0x611200); + + if (ram->base.ranks > 1) { + ram->fuc.r_mr[0] = ramfuc_reg2(0x1002c0, 0x1002c8); + ram->fuc.r_mr[1] = ramfuc_reg2(0x1002c4, 0x1002cc); + ram->fuc.r_mr[2] = ramfuc_reg2(0x1002e0, 0x1002e8); + ram->fuc.r_mr[3] = ramfuc_reg2(0x1002e4, 0x1002ec); + } else { + ram->fuc.r_mr[0] = ramfuc_reg(0x1002c0); + ram->fuc.r_mr[1] = ramfuc_reg(0x1002c4); + ram->fuc.r_mr[2] = ramfuc_reg(0x1002e0); + ram->fuc.r_mr[3] = ramfuc_reg(0x1002e4); + } + + ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func); + if (ret == 0) { + nv50_gpio_location(func.line, ®, &shift); + ram->fuc.r_gpioFBVREF = ramfuc_reg(reg); + } + + return 0; +} + +struct nvkm_oclass +gt215_ram_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = gt215_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = gt215_ram_init, + .fini = gt215_ram_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c new file mode 100644 index 000000000..abc18e89a --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c @@ -0,0 +1,101 @@ +/* + * Copyright 2013 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 "nv50.h" + +struct mcp77_ram_priv { + struct nvkm_ram base; + u64 poller_base; +}; + +static int +mcp77_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 datasize, + struct nvkm_object **pobject) +{ + u32 rsvd_head = ( 256 * 1024); /* vga memory */ + u32 rsvd_tail = (1024 * 1024); /* vbios etc */ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct mcp77_ram_priv *priv; + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.type = NV_MEM_TYPE_STOLEN; + priv->base.stolen = (u64)nv_rd32(pfb, 0x100e10) << 12; + priv->base.size = (u64)nv_rd32(pfb, 0x100e14) << 12; + + rsvd_tail += 0x1000; + priv->poller_base = priv->base.size - rsvd_tail; + + ret = nvkm_mm_init(&pfb->vram, rsvd_head >> 12, + (priv->base.size - (rsvd_head + rsvd_tail)) >> 12, + 1); + if (ret) + return ret; + + priv->base.get = nv50_ram_get; + priv->base.put = nv50_ram_put; + return 0; +} + +static int +mcp77_ram_init(struct nvkm_object *object) +{ + struct nvkm_fb *pfb = nvkm_fb(object); + struct mcp77_ram_priv *priv = (void *)object; + int ret; + u64 dniso, hostnb, flush; + + ret = nvkm_ram_init(&priv->base); + if (ret) + return ret; + + dniso = ((priv->base.size - (priv->poller_base + 0x00)) >> 5) - 1; + hostnb = ((priv->base.size - (priv->poller_base + 0x20)) >> 5) - 1; + flush = ((priv->base.size - (priv->poller_base + 0x40)) >> 5) - 1; + + /* Enable NISO poller for various clients and set their associated + * read address, only for MCP77/78 and MCP79/7A. (fd#25701) + */ + nv_wr32(pfb, 0x100c18, dniso); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00000001); + nv_wr32(pfb, 0x100c1c, hostnb); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00000002); + nv_wr32(pfb, 0x100c24, flush); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00010000); + return 0; +} + +struct nvkm_oclass +mcp77_ram_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = mcp77_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = mcp77_ram_init, + .fini = _nvkm_ram_fini, + }, +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c new file mode 100644 index 000000000..855de1617 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c @@ -0,0 +1,79 @@ +/* + * Copyright 2013 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 "priv.h" +#include "regsnv04.h" + +static int +nv04_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (boot0 & 0x00000100) { + ram->size = ((boot0 >> 12) & 0xf) * 2 + 2; + ram->size *= 1024 * 1024; + } else { + switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { + case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: + ram->size = 32 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: + ram->size = 16 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: + ram->size = 8 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: + ram->size = 4 * 1024 * 1024; + break; + } + } + + if ((boot0 & 0x00000038) <= 0x10) + ram->type = NV_MEM_TYPE_SGRAM; + else + ram->type = NV_MEM_TYPE_SDRAM; + + return 0; +} + +struct nvkm_oclass +nv04_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv04_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c new file mode 100644 index 000000000..3b8a1eda5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c @@ -0,0 +1,59 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv10_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + u32 cfg0 = nv_rd32(pfb, 0x100200); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (cfg0 & 0x00000001) + ram->type = NV_MEM_TYPE_DDR1; + else + ram->type = NV_MEM_TYPE_SDRAM; + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + return 0; +} + +struct nvkm_oclass +nv10_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv10_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c new file mode 100644 index 000000000..fbae05db4 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c @@ -0,0 +1,72 @@ +/* + * Copyright 2013 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 "priv.h" + +#include + +static int +nv1a_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + struct pci_dev *bridge; + u32 mem, mib; + int ret; + + bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); + if (!bridge) { + nv_fatal(pfb, "no bridge device\n"); + return -ENODEV; + } + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (nv_device(pfb)->chipset == 0x1a) { + pci_read_config_dword(bridge, 0x7c, &mem); + mib = ((mem >> 6) & 31) + 1; + } else { + pci_read_config_dword(bridge, 0x84, &mem); + mib = ((mem >> 4) & 127) + 1; + } + + ram->type = NV_MEM_TYPE_STOLEN; + ram->size = mib * 1024 * 1024; + return 0; +} + +struct nvkm_oclass +nv1a_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv1a_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c new file mode 100644 index 000000000..d9e7187bd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c @@ -0,0 +1,62 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv20_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + u32 pbus1218 = nv_rd32(pfb, 0x001218); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: ram->type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: ram->type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: ram->type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: ram->type = NV_MEM_TYPE_GDDR2; break; + } + ram->size = (nv_rd32(pfb, 0x10020c) & 0xff000000); + ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->tags = nv_rd32(pfb, 0x100320); + return 0; +} + +struct nvkm_oclass +nv20_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv20_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c new file mode 100644 index 000000000..3d31fa45c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c @@ -0,0 +1,212 @@ +/* + * Copyright 2013 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 "nv40.h" + +#include +#include +#include +#include +#include +#include +#include + +int +nv40_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nv40_ram *ram = (void *)pfb->ram; + struct nvbios_pll pll; + int N1, M1, N2, M2; + int log2P, ret; + + ret = nvbios_pll_parse(bios, 0x04, &pll); + if (ret) { + nv_error(pfb, "mclk pll data not found\n"); + return ret; + } + + ret = nv04_pll_calc(nv_subdev(pfb), &pll, freq, + &N1, &M1, &N2, &M2, &log2P); + if (ret < 0) + return ret; + + ram->ctrl = 0x80000000 | (log2P << 16); + ram->ctrl |= min(pll.bias_p + log2P, (int)pll.max_p) << 20; + if (N2 == M2) { + ram->ctrl |= 0x00000100; + ram->coef = (N1 << 8) | M1; + } else { + ram->ctrl |= 0x40000000; + ram->coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1; + } + + return 0; +} + +int +nv40_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nv40_ram *ram = (void *)pfb->ram; + struct bit_entry M; + u32 crtc_mask = 0; + u8 sr1[2]; + int i; + + /* determine which CRTCs are active, fetch VGA_SR1 for each */ + for (i = 0; i < 2; i++) { + u32 vbl = nv_rd32(pfb, 0x600808 + (i * 0x2000)); + u32 cnt = 0; + do { + if (vbl != nv_rd32(pfb, 0x600808 + (i * 0x2000))) { + nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01); + sr1[i] = nv_rd08(pfb, 0x0c03c5 + (i * 0x2000)); + if (!(sr1[i] & 0x20)) + crtc_mask |= (1 << i); + break; + } + udelay(1); + } while (cnt++ < 32); + } + + /* wait for vblank start on active crtcs, disable memory access */ + for (i = 0; i < 2; i++) { + if (!(crtc_mask & (1 << i))) + continue; + nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000); + nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000); + nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01); + nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20); + } + + /* prepare ram for reclocking */ + nv_wr32(pfb, 0x1002d4, 0x00000001); /* precharge */ + nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */ + nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */ + nv_mask(pfb, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */ + nv_wr32(pfb, 0x1002dc, 0x00000001); /* enable self-refresh */ + + /* change the PLL of each memory partition */ + nv_mask(pfb, 0x00c040, 0x0000c000, 0x00000000); + switch (nv_device(pfb)->chipset) { + case 0x40: + case 0x45: + case 0x41: + case 0x42: + case 0x47: + nv_mask(pfb, 0x004044, 0xc0771100, ram->ctrl); + nv_mask(pfb, 0x00402c, 0xc0771100, ram->ctrl); + nv_wr32(pfb, 0x004048, ram->coef); + nv_wr32(pfb, 0x004030, ram->coef); + case 0x43: + case 0x49: + case 0x4b: + nv_mask(pfb, 0x004038, 0xc0771100, ram->ctrl); + nv_wr32(pfb, 0x00403c, ram->coef); + default: + nv_mask(pfb, 0x004020, 0xc0771100, ram->ctrl); + nv_wr32(pfb, 0x004024, ram->coef); + break; + } + udelay(100); + nv_mask(pfb, 0x00c040, 0x0000c000, 0x0000c000); + + /* re-enable normal operation of memory controller */ + nv_wr32(pfb, 0x1002dc, 0x00000000); + nv_mask(pfb, 0x100210, 0x80000000, 0x80000000); + udelay(100); + + /* execute memory reset script from vbios */ + if (!bit_entry(bios, 'M', &M)) { + struct nvbios_init init = { + .subdev = nv_subdev(pfb), + .bios = bios, + .offset = nv_ro16(bios, M.offset + 0x00), + .execute = 1, + }; + + nvbios_exec(&init); + } + + /* make sure we're in vblank (hopefully the same one as before), and + * then re-enable crtc memory access + */ + for (i = 0; i < 2; i++) { + if (!(crtc_mask & (1 << i))) + continue; + nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000); + nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01); + nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i]); + } + + return 0; +} + +void +nv40_ram_tidy(struct nvkm_fb *pfb) +{ +} + +static int +nv40_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pbus1218 = nv_rd32(pfb, 0x001218); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: ram->base.type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: ram->base.type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: ram->base.type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: ram->base.type = NV_MEM_TYPE_DDR2; break; + } + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->base.tags = nv_rd32(pfb, 0x100320); + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + + +struct nvkm_oclass +nv40_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv40_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c new file mode 100644 index 000000000..33c612b13 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c @@ -0,0 +1,66 @@ +/* + * Copyright 2013 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 "nv40.h" + +static int +nv41_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pfb474 = nv_rd32(pfb, 0x100474); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (pfb474 & 0x00000004) + ram->base.type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + ram->base.type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + ram->base.type = NV_MEM_TYPE_DDR1; + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->base.tags = nv_rd32(pfb, 0x100320); + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + +struct nvkm_oclass +nv41_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv41_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c new file mode 100644 index 000000000..f575a7246 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c @@ -0,0 +1,64 @@ +/* + * Copyright 2013 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 "nv40.h" + +static int +nv44_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pfb474 = nv_rd32(pfb, 0x100474); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (pfb474 & 0x00000004) + ram->base.type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + ram->base.type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + ram->base.type = NV_MEM_TYPE_DDR1; + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + +struct nvkm_oclass +nv44_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv44_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c new file mode 100644 index 000000000..51b44cdb2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c @@ -0,0 +1,66 @@ +/* + * Copyright 2013 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 "nv40.h" + +static int +nv49_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nv40_ram *ram; + u32 pfb914 = nv_rd32(pfb, 0x100914); + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pfb914 & 0x00000003) { + case 0x00000000: ram->base.type = NV_MEM_TYPE_DDR1; break; + case 0x00000001: ram->base.type = NV_MEM_TYPE_DDR2; break; + case 0x00000002: ram->base.type = NV_MEM_TYPE_GDDR3; break; + case 0x00000003: break; + } + + ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->base.tags = nv_rd32(pfb, 0x100320); + ram->base.calc = nv40_ram_calc; + ram->base.prog = nv40_ram_prog; + ram->base.tidy = nv40_ram_tidy; + return 0; +} + +struct nvkm_oclass +nv49_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv49_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c new file mode 100644 index 000000000..f3ed1c60d --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c @@ -0,0 +1,54 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv4e_ram_create(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + int ret; + + ret = nvkm_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->type = NV_MEM_TYPE_STOLEN; + return 0; +} + +struct nvkm_oclass +nv4e_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv4e_ram_create, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c new file mode 100644 index 000000000..d2c81dd63 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c @@ -0,0 +1,465 @@ +/* + * Copyright 2013 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 "nv50.h" +#include "ramseq.h" + +#include +#include +#include +#include +#include +#include +#include + +struct nv50_ramseq { + struct hwsq base; + struct hwsq_reg r_0x002504; + struct hwsq_reg r_0x004008; + struct hwsq_reg r_0x00400c; + struct hwsq_reg r_0x00c040; + struct hwsq_reg r_0x100210; + struct hwsq_reg r_0x1002d0; + struct hwsq_reg r_0x1002d4; + struct hwsq_reg r_0x1002dc; + struct hwsq_reg r_0x100da0[8]; + struct hwsq_reg r_0x100e20; + struct hwsq_reg r_0x100e24; + struct hwsq_reg r_0x611200; + struct hwsq_reg r_timing[9]; + struct hwsq_reg r_mr[4]; +}; + +struct nv50_ram { + struct nvkm_ram base; + struct nv50_ramseq hwsq; +}; + +#define QFX5800NVA0 1 + +static int +nv50_ram_calc(struct nvkm_fb *pfb, u32 freq) +{ + struct nvkm_bios *bios = nvkm_bios(pfb); + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + struct nvbios_perfE perfE; + struct nvbios_pll mpll; + struct { + u32 data; + u8 size; + } ramcfg, timing; + u8 ver, hdr, cnt, len, strap; + int N1, M1, N2, M2, P; + int ret, i; + + /* lookup closest matching performance table entry for frequency */ + i = 0; + do { + ramcfg.data = nvbios_perfEp(bios, i++, &ver, &hdr, &cnt, + &ramcfg.size, &perfE); + if (!ramcfg.data || (ver < 0x25 || ver >= 0x40) || + (ramcfg.size < 2)) { + nv_error(pfb, "invalid/missing perftab entry\n"); + return -EINVAL; + } + } while (perfE.memory < freq); + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(nv_subdev(pfb)); + if (strap >= cnt) { + nv_error(pfb, "invalid ramcfg strap\n"); + return -EINVAL; + } + + ramcfg.data += hdr + (strap * ramcfg.size); + + /* lookup memory timings, if bios says they're present */ + strap = nv_ro08(bios, ramcfg.data + 0x01); + if (strap != 0xff) { + timing.data = nvbios_timingEe(bios, strap, &ver, &hdr, + &cnt, &len); + if (!timing.data || ver != 0x10 || hdr < 0x12) { + nv_error(pfb, "invalid/missing timing entry " + "%02x %04x %02x %02x\n", + strap, timing.data, ver, hdr); + return -EINVAL; + } + } else { + timing.data = 0; + } + + ret = ram_init(hwsq, nv_subdev(pfb)); + if (ret) + return ret; + + ram_wait(hwsq, 0x01, 0x00); /* wait for !vblank */ + ram_wait(hwsq, 0x01, 0x01); /* wait for vblank */ + ram_wr32(hwsq, 0x611200, 0x00003300); + ram_wr32(hwsq, 0x002504, 0x00000001); /* block fifo */ + ram_nsec(hwsq, 8000); + ram_setf(hwsq, 0x10, 0x00); /* disable fb */ + ram_wait(hwsq, 0x00, 0x01); /* wait for fb disabled */ + + ram_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge */ + ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */ + ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */ + ram_wr32(hwsq, 0x100210, 0x00000000); /* disable auto-refresh */ + ram_wr32(hwsq, 0x1002dc, 0x00000001); /* enable self-refresh */ + + ret = nvbios_pll_parse(bios, 0x004008, &mpll); + mpll.vco2.max_freq = 0; + if (ret == 0) { + ret = nv04_pll_calc(nv_subdev(pfb), &mpll, freq, + &N1, &M1, &N2, &M2, &P); + if (ret == 0) + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + ram_mask(hwsq, 0x00c040, 0xc000c000, 0x0000c000); + ram_mask(hwsq, 0x004008, 0x00000200, 0x00000200); + ram_mask(hwsq, 0x00400c, 0x0000ffff, (N1 << 8) | M1); + ram_mask(hwsq, 0x004008, 0x81ff0000, 0x80000000 | (mpll.bias_p << 19) | + (P << 22) | (P << 16)); +#if QFX5800NVA0 + for (i = 0; i < 8; i++) + ram_mask(hwsq, 0x100da0[i], 0x00000000, 0x00000000); /*XXX*/ +#endif + ram_nsec(hwsq, 96000); /*XXX*/ + ram_mask(hwsq, 0x004008, 0x00002200, 0x00002000); + + ram_wr32(hwsq, 0x1002dc, 0x00000000); /* disable self-refresh */ + ram_wr32(hwsq, 0x100210, 0x80000000); /* enable auto-refresh */ + + ram_nsec(hwsq, 12000); + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + ram_nuke(hwsq, mr[0]); /* force update */ + ram_mask(hwsq, mr[0], 0x000, 0x000); + break; + case NV_MEM_TYPE_GDDR3: + ram_mask(hwsq, mr[2], 0x000, 0x000); + ram_nuke(hwsq, mr[0]); /* force update */ + ram_mask(hwsq, mr[0], 0x000, 0x000); + break; + default: + break; + } + + ram_mask(hwsq, timing[3], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[1], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[6], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[7], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[8], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[2], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[4], 0x00000000, 0x00000000); /*XXX*/ + ram_mask(hwsq, timing[5], 0x00000000, 0x00000000); /*XXX*/ + + ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/ + +#if QFX5800NVA0 + ram_nuke(hwsq, 0x100e24); + ram_mask(hwsq, 0x100e24, 0x00000000, 0x00000000); + ram_nuke(hwsq, 0x100e20); + ram_mask(hwsq, 0x100e20, 0x00000000, 0x00000000); +#endif + + ram_mask(hwsq, mr[0], 0x100, 0x100); + ram_mask(hwsq, mr[0], 0x100, 0x000); + + ram_setf(hwsq, 0x10, 0x01); /* enable fb */ + ram_wait(hwsq, 0x00, 0x00); /* wait for fb enabled */ + ram_wr32(hwsq, 0x611200, 0x00003330); + ram_wr32(hwsq, 0x002504, 0x00000000); /* un-block fifo */ + return 0; +} + +static int +nv50_ram_prog(struct nvkm_fb *pfb) +{ + struct nvkm_device *device = nv_device(pfb); + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + + ram_exec(hwsq, nvkm_boolopt(device->cfgopt, "NvMemExec", true)); + return 0; +} + +static void +nv50_ram_tidy(struct nvkm_fb *pfb) +{ + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + ram_exec(hwsq, false); +} + +void +__nv50_ram_put(struct nvkm_fb *pfb, struct nvkm_mem *mem) +{ + struct nvkm_mm_node *this; + + while (!list_empty(&mem->regions)) { + this = list_first_entry(&mem->regions, typeof(*this), rl_entry); + + list_del(&this->rl_entry); + nvkm_mm_free(&pfb->vram, &this); + } + + nvkm_mm_free(&pfb->tags, &mem->tag); +} + +void +nv50_ram_put(struct nvkm_fb *pfb, struct nvkm_mem **pmem) +{ + struct nvkm_mem *mem = *pmem; + + *pmem = NULL; + if (unlikely(mem == NULL)) + return; + + mutex_lock(&pfb->base.mutex); + __nv50_ram_put(pfb, mem); + mutex_unlock(&pfb->base.mutex); + + kfree(mem); +} + +int +nv50_ram_get(struct nvkm_fb *pfb, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nvkm_mem **pmem) +{ + struct nvkm_mm *heap = &pfb->vram; + struct nvkm_mm *tags = &pfb->tags; + struct nvkm_mm_node *r; + struct nvkm_mem *mem; + int comp = (memtype & 0x300) >> 8; + int type = (memtype & 0x07f); + int back = (memtype & 0x800); + int min, max, ret; + + max = (size >> 12); + min = ncmin ? (ncmin >> 12) : max; + align >>= 12; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + mutex_lock(&pfb->base.mutex); + if (comp) { + if (align == 16) { + int n = (max >> 4) * comp; + + ret = nvkm_mm_head(tags, 0, 1, n, n, 1, &mem->tag); + if (ret) + mem->tag = NULL; + } + + if (unlikely(!mem->tag)) + comp = 0; + } + + INIT_LIST_HEAD(&mem->regions); + mem->memtype = (comp << 7) | type; + mem->size = max; + + type = nv50_fb_memtype[type]; + do { + if (back) + ret = nvkm_mm_tail(heap, 0, type, max, min, align, &r); + else + ret = nvkm_mm_head(heap, 0, type, max, min, align, &r); + if (ret) { + mutex_unlock(&pfb->base.mutex); + pfb->ram->put(pfb, &mem); + return ret; + } + + list_add_tail(&r->rl_entry, &mem->regions); + max -= r->length; + } while (max); + mutex_unlock(&pfb->base.mutex); + + r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry); + mem->offset = (u64)r->offset << 12; + *pmem = mem; + return 0; +} + +static u32 +nv50_fb_vram_rblock(struct nvkm_fb *pfb, struct nvkm_ram *ram) +{ + int colbits, rowbitsa, rowbitsb, banks; + u64 rowsize, predicted; + u32 r0, r4, rt, rblock_size; + + r0 = nv_rd32(pfb, 0x100200); + r4 = nv_rd32(pfb, 0x100204); + rt = nv_rd32(pfb, 0x100250); + nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", + r0, r4, rt, nv_rd32(pfb, 0x001540)); + + colbits = (r4 & 0x0000f000) >> 12; + rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; + rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; + banks = 1 << (((r4 & 0x03000000) >> 24) + 2); + + rowsize = ram->parts * banks * (1 << colbits) * 8; + predicted = rowsize << rowbitsa; + if (r0 & 0x00000004) + predicted += rowsize << rowbitsb; + + if (predicted != ram->size) { + nv_warn(pfb, "memory controller reports %d MiB VRAM\n", + (u32)(ram->size >> 20)); + } + + rblock_size = rowsize; + if (rt & 1) + rblock_size *= 3; + + nv_debug(pfb, "rblock %d bytes\n", rblock_size); + return rblock_size; +} + +int +nv50_ram_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, int length, void **pobject) +{ + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + struct nvkm_bios *bios = nvkm_bios(parent); + struct nvkm_fb *pfb = nvkm_fb(parent); + struct nvkm_ram *ram; + int ret; + + ret = nvkm_ram_create_(parent, engine, oclass, length, pobject); + ram = *pobject; + if (ret) + return ret; + + ram->size = nv_rd32(pfb, 0x10020c); + ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32); + + ram->part_mask = (nv_rd32(pfb, 0x001540) & 0x00ff0000) >> 16; + ram->parts = hweight8(ram->part_mask); + + switch (nv_rd32(pfb, 0x100714) & 0x00000007) { + case 0: ram->type = NV_MEM_TYPE_DDR1; break; + case 1: + if (nvkm_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3) + ram->type = NV_MEM_TYPE_DDR3; + else + ram->type = NV_MEM_TYPE_DDR2; + break; + case 2: ram->type = NV_MEM_TYPE_GDDR3; break; + case 3: ram->type = NV_MEM_TYPE_GDDR4; break; + case 4: ram->type = NV_MEM_TYPE_GDDR5; break; + default: + break; + } + + ret = nvkm_mm_init(&pfb->vram, rsvd_head, (ram->size >> 12) - + (rsvd_head + rsvd_tail), + nv50_fb_vram_rblock(pfb, ram) >> 12); + if (ret) + return ret; + + ram->ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1; + ram->tags = nv_rd32(pfb, 0x100320); + ram->get = nv50_ram_get; + ram->put = nv50_ram_put; + return 0; +} + +static int +nv50_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 datasize, + struct nvkm_object **pobject) +{ + struct nv50_ram *ram; + int ret, i; + + ret = nv50_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR2: + case NV_MEM_TYPE_GDDR3: + ram->base.calc = nv50_ram_calc; + ram->base.prog = nv50_ram_prog; + ram->base.tidy = nv50_ram_tidy; + break; + default: + nv_warn(ram, "reclocking of this ram type unsupported\n"); + return 0; + } + + ram->hwsq.r_0x002504 = hwsq_reg(0x002504); + ram->hwsq.r_0x00c040 = hwsq_reg(0x00c040); + ram->hwsq.r_0x004008 = hwsq_reg(0x004008); + ram->hwsq.r_0x00400c = hwsq_reg(0x00400c); + ram->hwsq.r_0x100210 = hwsq_reg(0x100210); + ram->hwsq.r_0x1002d0 = hwsq_reg(0x1002d0); + ram->hwsq.r_0x1002d4 = hwsq_reg(0x1002d4); + ram->hwsq.r_0x1002dc = hwsq_reg(0x1002dc); + for (i = 0; i < 8; i++) + ram->hwsq.r_0x100da0[i] = hwsq_reg(0x100da0 + (i * 0x04)); + ram->hwsq.r_0x100e20 = hwsq_reg(0x100e20); + ram->hwsq.r_0x100e24 = hwsq_reg(0x100e24); + ram->hwsq.r_0x611200 = hwsq_reg(0x611200); + + for (i = 0; i < 9; i++) + ram->hwsq.r_timing[i] = hwsq_reg(0x100220 + (i * 0x04)); + + if (ram->base.ranks > 1) { + ram->hwsq.r_mr[0] = hwsq_reg2(0x1002c0, 0x1002c8); + ram->hwsq.r_mr[1] = hwsq_reg2(0x1002c4, 0x1002cc); + ram->hwsq.r_mr[2] = hwsq_reg2(0x1002e0, 0x1002e8); + ram->hwsq.r_mr[3] = hwsq_reg2(0x1002e4, 0x1002ec); + } else { + ram->hwsq.r_mr[0] = hwsq_reg(0x1002c0); + ram->hwsq.r_mr[1] = hwsq_reg(0x1002c4); + ram->hwsq.r_mr[2] = hwsq_reg(0x1002e0); + ram->hwsq.r_mr[3] = hwsq_reg(0x1002e4); + } + + return 0; +} + +struct nvkm_oclass +nv50_ram_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = nv50_ram_ctor, + .dtor = _nvkm_ram_dtor, + .init = _nvkm_ram_init, + .fini = _nvkm_ram_fini, + } +}; diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h new file mode 100644 index 000000000..0f1f97ccd --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h @@ -0,0 +1,15 @@ +#ifndef __NVKM_FBRAM_SEQ_H__ +#define __NVKM_FBRAM_SEQ_H__ +#include + +#define ram_init(s,p) hwsq_init(&(s)->base, (p)) +#define ram_exec(s,e) hwsq_exec(&(s)->base, (e)) +#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_rd32(s,r) hwsq_rd32(&(s)->base, &(s)->r_##r) +#define ram_wr32(s,r,d) hwsq_wr32(&(s)->base, &(s)->r_##r, (d)) +#define ram_nuke(s,r) hwsq_nuke(&(s)->base, &(s)->r_##r) +#define ram_mask(s,r,m,d) hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define ram_setf(s,f,d) hwsq_setf(&(s)->base, (f), (d)) +#define ram_wait(s,f,d) hwsq_wait(&(s)->base, (f), (d)) +#define ram_nsec(s,n) hwsq_nsec(&(s)->base, (n)) +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h new file mode 100644 index 000000000..1f865f615 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h @@ -0,0 +1,22 @@ +#ifndef __NVKM_FB_REGS_04_H__ +#define __NVKM_FB_REGS_04_H__ + +#define NV04_PFB_BOOT_0 0x00100000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 +# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 +# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 +# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 +# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 +#define NV04_PFB_CFG0 0x00100200 + +#endif diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c new file mode 100644 index 000000000..afab42df2 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c @@ -0,0 +1,93 @@ +/* + * Copyright 2014 Roy Spliet + * + * 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: Roy Spliet + * Ben Skeggs + */ +#include "priv.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramddr2_cl[] = { + { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 7 }, + { -1 } +}; + +static const struct ramxlat +ramddr2_wr[] = { + { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 6 }, + { -1 } +}; + +int +nvkm_sddr2_calc(struct nvkm_ram *ram) +{ + int CL, WR, DLL = 0, ODT = 0; + + switch (ram->next->bios.timing_ver) { + case 0x10: + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_DLLoff; + ODT = ram->next->bios.timing_10_ODT & 3; + break; + case 0x20: + CL = (ram->next->bios.timing[1] & 0x0000001f); + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + break; + default: + return -ENOSYS; + } + + CL = ramxlat(ramddr2_cl, CL); + WR = ramxlat(ramddr2_wr, WR); + if (CL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf70; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x07) << 4; + + ram->mr[1] &= ~0x045; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= !DLL; + return 0; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c new file mode 100644 index 000000000..10844355c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c @@ -0,0 +1,119 @@ +/* + * Copyright 2013 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 + * Roy Spliet + */ +#include "priv.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramddr3_cl[] = { + { 5, 2 }, { 6, 4 }, { 7, 6 }, { 8, 8 }, { 9, 10 }, { 10, 12 }, + { 11, 14 }, + /* the below are mentioned in some, but not all, ddr3 docs */ + { 12, 1 }, { 13, 3 }, { 14, 5 }, + { -1 } +}; + +static const struct ramxlat +ramddr3_wr[] = { + { 5, 1 }, { 6, 2 }, { 7, 3 }, { 8, 4 }, { 10, 5 }, { 12, 6 }, + /* the below are mentioned in some, but not all, ddr3 docs */ + { 14, 7 }, { 16, 0 }, + { -1 } +}; + +static const struct ramxlat +ramddr3_cwl[] = { + { 5, 0 }, { 6, 1 }, { 7, 2 }, { 8, 3 }, + /* the below are mentioned in some, but not all, ddr3 docs */ + { 9, 4 }, + { -1 } +}; + +int +nvkm_sddr3_calc(struct nvkm_ram *ram) +{ + int CWL, CL, WR, DLL = 0, ODT = 0; + + switch (ram->next->bios.timing_ver) { + case 0x10: + if (ram->next->bios.timing_hdr < 0x17) { + /* XXX: NV50: Get CWL from the timing register */ + return -ENOSYS; + } + CWL = ram->next->bios.timing_10_CWL; + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_DLLoff; + ODT = ram->next->bios.timing_10_ODT; + break; + case 0x20: + CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0; + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + /* XXX: Get these values from the VBIOS instead */ + DLL = !(ram->mr[1] & 0x1); + ODT = (ram->mr[1] & 0x004) >> 2 | + (ram->mr[1] & 0x040) >> 5 | + (ram->mr[1] & 0x200) >> 7; + break; + default: + return -ENOSYS; + } + + CWL = ramxlat(ramddr3_cwl, CWL); + CL = ramxlat(ramddr3_cl, CL); + WR = ramxlat(ramddr3_wr, WR); + if (CL < 0 || CWL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf74; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x0e) << 3; + ram->mr[0] |= (CL & 0x01) << 2; + + ram->mr[1] &= ~0x245; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= (ODT & 0x4) << 7; + ram->mr[1] |= !DLL; + + ram->mr[2] &= ~0x038; + ram->mr[2] |= (CWL & 0x07) << 3; + return 0; +} -- cgit 1.2.3-korg