diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/devinit')
22 files changed, 2320 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild new file mode 100644 index 000000000..d1abb6484 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/devinit/base.o +nvkm-y += nvkm/subdev/devinit/nv04.o +nvkm-y += nvkm/subdev/devinit/nv05.o +nvkm-y += nvkm/subdev/devinit/nv10.o +nvkm-y += nvkm/subdev/devinit/nv1a.o +nvkm-y += nvkm/subdev/devinit/nv20.o +nvkm-y += nvkm/subdev/devinit/nv50.o +nvkm-y += nvkm/subdev/devinit/g84.o +nvkm-y += nvkm/subdev/devinit/g98.o +nvkm-y += nvkm/subdev/devinit/gt215.o +nvkm-y += nvkm/subdev/devinit/mcp89.o +nvkm-y += nvkm/subdev/devinit/gf100.o +nvkm-y += nvkm/subdev/devinit/gm107.o +nvkm-y += nvkm/subdev/devinit/gm200.o +nvkm-y += nvkm/subdev/devinit/gv100.o +nvkm-y += nvkm/subdev/devinit/tu102.o +nvkm-y += nvkm/subdev/devinit/ga100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c new file mode 100644 index 000000000..dd4981708 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c @@ -0,0 +1,135 @@ +/* + * 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 <core/option.h> +#include <subdev/vga.h> + +u32 +nvkm_devinit_mmio(struct nvkm_devinit *init, u32 addr) +{ + if (init->func->mmio) + addr = init->func->mmio(init, addr); + return addr; +} + +int +nvkm_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 khz) +{ + return init->func->pll_set(init, type, khz); +} + +void +nvkm_devinit_meminit(struct nvkm_devinit *init) +{ + if (init->func->meminit) + init->func->meminit(init); +} + +u64 +nvkm_devinit_disable(struct nvkm_devinit *init) +{ + if (init && init->func->disable) + return init->func->disable(init); + return 0; +} + +int +nvkm_devinit_post(struct nvkm_devinit *init) +{ + int ret = 0; + if (init && init->func->post) + ret = init->func->post(init, init->post); + nvkm_devinit_disable(init); + return ret; +} + +static int +nvkm_devinit_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + /* force full reinit on resume */ + if (suspend) + init->post = true; + return 0; +} + +static int +nvkm_devinit_preinit(struct nvkm_subdev *subdev) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + + if (init->func->preinit) + init->func->preinit(init); + + /* Override the post flag during the first call if NvForcePost is set */ + if (init->force_post) { + init->post = init->force_post; + init->force_post = false; + } + + /* unlock the extended vga crtc regs */ + nvkm_lockvgac(subdev->device, false); + return 0; +} + +static int +nvkm_devinit_init(struct nvkm_subdev *subdev) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + if (init->func->init) + init->func->init(init); + return 0; +} + +static void * +nvkm_devinit_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + void *data = init; + + if (init->func->dtor) + data = init->func->dtor(init); + + /* lock crtc regs */ + nvkm_lockvgac(subdev->device, true); + return data; +} + +static const struct nvkm_subdev_func +nvkm_devinit = { + .dtor = nvkm_devinit_dtor, + .preinit = nvkm_devinit_preinit, + .init = nvkm_devinit_init, + .fini = nvkm_devinit_fini, +}; + +void +nvkm_devinit_ctor(const struct nvkm_devinit_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_devinit *init) +{ + nvkm_subdev_ctor(&nvkm_devinit, device, type, inst, &init->subdev); + init->func = func; + init->force_post = nvkm_boolopt(device->cfgopt, "NvForcePost", false); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h new file mode 100644 index 000000000..6c5bbff12 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h @@ -0,0 +1,83 @@ +/* + * 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 <subdev/fb/regsnv04.h> + +#define NV04_PFB_DEBUG_0 0x00100080 +# define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001 +# define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010 +# define NV04_PFB_DEBUG_0_REFRESH_COUNTX64 0x00003f00 +# define NV04_PFB_DEBUG_0_REFRESH_SLOW_CLK 0x00004000 +# define NV04_PFB_DEBUG_0_SAFE_MODE 0x00008000 +# define NV04_PFB_DEBUG_0_ALOM_ENABLE 0x00010000 +# define NV04_PFB_DEBUG_0_CASOE 0x00100000 +# define NV04_PFB_DEBUG_0_CKE_INVERT 0x10000000 +# define NV04_PFB_DEBUG_0_REFINC 0x20000000 +# define NV04_PFB_DEBUG_0_SAVE_POWER_OFF 0x40000000 +#define NV04_PFB_CFG0 0x00100200 +# define NV04_PFB_CFG0_SCRAMBLE 0x20000000 +#define NV04_PFB_CFG1 0x00100204 +#define NV04_PFB_SCRAMBLE(i) (0x00100400 + 4 * (i)) + +#define NV10_PFB_REFCTRL 0x00100210 +# define NV10_PFB_REFCTRL_VALID_1 (1 << 31) + +static inline struct io_mapping * +fbmem_init(struct nvkm_device *dev) +{ + return io_mapping_create_wc(dev->func->resource_addr(dev, 1), + dev->func->resource_size(dev, 1)); +} + +static inline void +fbmem_fini(struct io_mapping *fb) +{ + io_mapping_free(fb); +} + +static inline u32 +fbmem_peek(struct io_mapping *fb, u32 off) +{ + u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK); + u32 val = ioread32(p + (off & ~PAGE_MASK)); + io_mapping_unmap_atomic(p); + return val; +} + +static inline void +fbmem_poke(struct io_mapping *fb, u32 off, u32 val) +{ + u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK); + iowrite32(val, p + (off & ~PAGE_MASK)); + wmb(); + io_mapping_unmap_atomic(p); +} + +static inline bool +fbmem_readback(struct io_mapping *fb, u32 off, u32 val) +{ + fbmem_poke(fb, off, val); + return val == fbmem_peek(fb, off); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c new file mode 100644 index 000000000..c224702b7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c @@ -0,0 +1,68 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/init.h> + +static u64 +g84_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MPEG, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_VP, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_BSP, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_CIPHER, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_BSP, 0); + if (!(r00154c & 0x00000040)) + nvkm_subdev_disable(device, NVKM_ENGINE_CIPHER, 0); + + return disable; +} + +static const struct nvkm_devinit_func +g84_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = nv50_devinit_pll_set, + .disable = g84_devinit_disable, +}; + +int +g84_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&g84_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c new file mode 100644 index 000000000..8977483a9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.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 "nv50.h" + +#include <subdev/bios.h> +#include <subdev/bios/init.h> + +static u64 +g98_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (!(r00154c & 0x00000040)) + nvkm_subdev_disable(device, NVKM_ENGINE_SEC, 0); + + return 0ULL; +} + +static const struct nvkm_devinit_func +g98_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = nv50_devinit_pll_set, + .disable = g98_devinit_disable, +}; + +int +g98_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&g98_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c new file mode 100644 index 000000000..6b280b05c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c @@ -0,0 +1,77 @@ +/* + * Copyright 2021 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. + */ +#include "nv50.h" + +#include <subdev/bios.h> +#include <subdev/bios/pll.h> +#include <subdev/clk/pll.h> + +static int +ga100_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int head = type - PLL_VPLL0; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_wr32(device, 0x00ef00 + (head * 0x40), 0x02080004); + nvkm_wr32(device, 0x00ef18 + (head * 0x40), (N << 16) | fN); + nvkm_wr32(device, 0x00ef04 + (head * 0x40), (P << 16) | M); + nvkm_wr32(device, 0x00e9c0 + (head * 0x04), 0x00000001); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct nvkm_devinit_func +ga100_devinit = { + .init = nv50_devinit_init, + .post = tu102_devinit_post, + .pll_set = ga100_devinit_pll_set, +}; + +int +ga100_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&ga100_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c new file mode 100644 index 000000000..5b7cb1fe7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.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 "nv50.h" + +#include <subdev/bios.h> +#include <subdev/bios/init.h> +#include <subdev/bios/pll.h> +#include <subdev/clk/pll.h> + +int +gf100_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_mask(device, info.reg + 0x0c, 0x00000000, 0x00000100); + nvkm_wr32(device, info.reg + 0x04, (P << 16) | (N << 8) | M); + nvkm_wr32(device, info.reg + 0x10, fN << 16); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static u64 +gf100_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r022500 = nvkm_rd32(device, 0x022500); + + if (r022500 & 0x00000001) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + + if (r022500 & 0x00000002) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (r022500 & 0x00000004) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (r022500 & 0x00000008) + nvkm_subdev_disable(device, NVKM_ENGINE_MSENC, 0); + if (r022500 & 0x00000100) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + if (r022500 & 0x00000200) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 1); + + return 0ULL; +} + +void +gf100_devinit_preinit(struct nvkm_devinit *base) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + + /* + * This bit is set by devinit, and flips back to 0 on suspend. We + * can use it as a reliable way to know whether we should run devinit. + */ + base->post = ((nvkm_rd32(device, 0x2240c) & BIT(1)) == 0); +} + +static const struct nvkm_devinit_func +gf100_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = gf100_devinit_pll_set, + .disable = gf100_devinit_disable, +}; + +int +gf100_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gf100_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c new file mode 100644 index 000000000..8955af270 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c @@ -0,0 +1,60 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/init.h> + +u64 +gm107_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r021c00 = nvkm_rd32(device, 0x021c00); + u32 r021c04 = nvkm_rd32(device, 0x021c04); + + if (r021c00 & 0x00000001) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + if (r021c00 & 0x00000004) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 2); + if (r021c04 & 0x00000001) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + + return 0ULL; +} + +static const struct nvkm_devinit_func +gm107_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = gf100_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +gm107_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gm107_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c new file mode 100644 index 000000000..a308b9bde --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c @@ -0,0 +1,186 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/pmu.h> +#include <subdev/timer.h> + +static void +pmu_code(struct nv50_devinit *init, u32 pmu, u32 img, u32 len, bool sec) +{ + struct nvkm_device *device = init->base.subdev.device; + struct nvkm_bios *bios = device->bios; + int i; + + nvkm_wr32(device, 0x10a180, 0x01000000 | (sec ? 0x10000000 : 0) | pmu); + for (i = 0; i < len; i += 4) { + if ((i & 0xff) == 0) + nvkm_wr32(device, 0x10a188, (pmu + i) >> 8); + nvkm_wr32(device, 0x10a184, nvbios_rd32(bios, img + i)); + } + + while (i & 0xff) { + nvkm_wr32(device, 0x10a184, 0x00000000); + i += 4; + } +} + +static void +pmu_data(struct nv50_devinit *init, u32 pmu, u32 img, u32 len) +{ + struct nvkm_device *device = init->base.subdev.device; + struct nvkm_bios *bios = device->bios; + int i; + + nvkm_wr32(device, 0x10a1c0, 0x01000000 | pmu); + for (i = 0; i < len; i += 4) + nvkm_wr32(device, 0x10a1c4, nvbios_rd32(bios, img + i)); +} + +static u32 +pmu_args(struct nv50_devinit *init, u32 argp, u32 argi) +{ + struct nvkm_device *device = init->base.subdev.device; + nvkm_wr32(device, 0x10a1c0, argp); + nvkm_wr32(device, 0x10a1c0, nvkm_rd32(device, 0x10a1c4) + argi); + return nvkm_rd32(device, 0x10a1c4); +} + +static void +pmu_exec(struct nv50_devinit *init, u32 init_addr) +{ + struct nvkm_device *device = init->base.subdev.device; + nvkm_wr32(device, 0x10a104, init_addr); + nvkm_wr32(device, 0x10a10c, 0x00000000); + nvkm_wr32(device, 0x10a100, 0x00000002); +} + +static int +pmu_load(struct nv50_devinit *init, u8 type, bool post, + u32 *init_addr_pmu, u32 *args_addr_pmu) +{ + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_pmuR pmu; + + if (!nvbios_pmuRm(bios, type, &pmu)) + return -EINVAL; + + if (!post) + return 0; + + pmu_code(init, pmu.boot_addr_pmu, pmu.boot_addr, pmu.boot_size, false); + pmu_code(init, pmu.code_addr_pmu, pmu.code_addr, pmu.code_size, true); + pmu_data(init, pmu.data_addr_pmu, pmu.data_addr, pmu.data_size); + + if (init_addr_pmu) { + *init_addr_pmu = pmu.init_addr_pmu; + *args_addr_pmu = pmu.args_addr_pmu; + return 0; + } + + return pmu_exec(init, pmu.init_addr_pmu), 0; +} + +void +gm200_devinit_preos(struct nv50_devinit *init, bool post) +{ + /* Optional: Execute PRE_OS application on PMU, which should at + * least take care of fans until a full PMU has been loaded. + */ + pmu_load(init, 0x01, post, NULL, NULL); +} + +int +gm200_devinit_post(struct nvkm_devinit *base, bool post) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct bit_entry bit_I; + u32 exec, args; + int ret; + + if (bit_entry(bios, 'I', &bit_I) || bit_I.version != 1 || + bit_I.length < 0x1c) { + nvkm_error(subdev, "VBIOS PMU init data not found\n"); + return -EINVAL; + } + + /* Upload DEVINIT application from VBIOS onto PMU. */ + ret = pmu_load(init, 0x04, post, &exec, &args); + if (ret) { + nvkm_error(subdev, "VBIOS PMU/DEVINIT not found\n"); + return ret; + } + + /* Upload tables required by opcodes in boot scripts. */ + if (post) { + u32 pmu = pmu_args(init, args + 0x08, 0x08); + u32 img = nvbios_rd16(bios, bit_I.offset + 0x14); + u32 len = nvbios_rd16(bios, bit_I.offset + 0x16); + pmu_data(init, pmu, img, len); + } + + /* Upload boot scripts. */ + if (post) { + u32 pmu = pmu_args(init, args + 0x08, 0x10); + u32 img = nvbios_rd16(bios, bit_I.offset + 0x18); + u32 len = nvbios_rd16(bios, bit_I.offset + 0x1a); + pmu_data(init, pmu, img, len); + } + + /* Execute DEVINIT. */ + if (post) { + nvkm_wr32(device, 0x10a040, 0x00005000); + pmu_exec(init, exec); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x10a040) & 0x00002000) + break; + ) < 0) + return -ETIMEDOUT; + } + + gm200_devinit_preos(init, post); + return 0; +} + +static const struct nvkm_devinit_func +gm200_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = gm200_devinit_post, + .pll_set = gf100_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +gm200_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gm200_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c new file mode 100644 index 000000000..3d0ab86c3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c @@ -0,0 +1,152 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/init.h> +#include <subdev/bios/pll.h> +#include <subdev/clk/pll.h> + +int +gt215_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nvkm_wr32(device, info.reg + 0, 0x50000610); + nvkm_mask(device, info.reg + 4, 0x003fffff, + (P << 16) | (M << 8) | N); + nvkm_wr32(device, info.reg + 8, fN); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static u64 +gt215_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (!(r00154c & 0x00000200)) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + + return 0ULL; +} + +static u32 +gt215_devinit_mmio_part[] = { + 0x100720, 0x1008bc, 4, + 0x100a20, 0x100adc, 4, + 0x100d80, 0x100ddc, 4, + 0x110000, 0x110f9c, 4, + 0x111000, 0x11103c, 8, + 0x111080, 0x1110fc, 4, + 0x111120, 0x1111fc, 4, + 0x111300, 0x1114bc, 4, + 0, +}; + +static u32 +gt215_devinit_mmio(struct nvkm_devinit *base, u32 addr) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_device *device = init->base.subdev.device; + u32 *mmio = gt215_devinit_mmio_part; + + /* the init tables on some boards have INIT_RAM_RESTRICT_ZM_REG_GROUP + * instructions which touch registers that may not even exist on + * some configurations (Quadro 400), which causes the register + * interface to screw up for some amount of time after attempting to + * write to one of these, and results in all sorts of things going + * horribly wrong. + * + * the binary driver avoids touching these registers at all, however, + * the video bios doesn't care and does what the scripts say. it's + * presumed that the io-port access to init registers isn't effected + * by the screw-up bug mentioned above. + * + * really, a new opcode should've been invented to handle these + * requirements, but whatever, it's too late for that now. + */ + while (mmio[0]) { + if (addr >= mmio[0] && addr <= mmio[1]) { + u32 part = (addr / mmio[2]) & 7; + if (!init->r001540) + init->r001540 = nvkm_rd32(device, 0x001540); + if (part >= hweight8((init->r001540 >> 16) & 0xff)) + return ~0; + return addr; + } + mmio += 3; + } + + return addr; +} + +static const struct nvkm_devinit_func +gt215_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .mmio = gt215_devinit_mmio, + .pll_set = gt215_devinit_pll_set, + .disable = gt215_devinit_disable, +}; + +int +gt215_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(>215_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c new file mode 100644 index 000000000..b4d168851 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c @@ -0,0 +1,79 @@ +/* + * Copyright 2018 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. + */ +#include "nv50.h" + +#include <subdev/bios.h> +#include <subdev/bios/pll.h> +#include <subdev/clk/pll.h> + +static int +gv100_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int head = type - PLL_VPLL0; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_wr32(device, 0x00ef10 + (head * 0x40), fN << 16); + nvkm_wr32(device, 0x00ef04 + (head * 0x40), (P << 16) | + (N << 8) | + (M << 0)); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct nvkm_devinit_func +gv100_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = gm200_devinit_post, + .pll_set = gv100_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +gv100_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gv100_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c new file mode 100644 index 000000000..a9cdf2411 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c @@ -0,0 +1,67 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/init.h> + +static u64 +mcp89_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (!(r00154c & 0x00000040)) + nvkm_subdev_disable(device, NVKM_ENGINE_VIC, 0); + if (!(r00154c & 0x00000200)) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + + return 0; +} + +static const struct nvkm_devinit_func +mcp89_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = gt215_devinit_pll_set, + .disable = mcp89_devinit_disable, +}; + +int +mcp89_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&mcp89_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c new file mode 100644 index 000000000..88bc890f8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c @@ -0,0 +1,465 @@ +/* + * 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 "fbmem.h" + +#include <subdev/bios.h> +#include <subdev/bios/init.h> +#include <subdev/bios/pll.h> +#include <subdev/clk/pll.h> +#include <subdev/vga.h> + +static void +nv04_devinit_meminit(struct nvkm_devinit *init) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + u32 patt = 0xdeadbeef; + struct io_mapping *fb; + int i; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + /* Sequencer and refresh off */ + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) | 0x20); + nvkm_mask(device, NV04_PFB_DEBUG_0, 0, NV04_PFB_DEBUG_0_REFRESH_OFF); + + nvkm_mask(device, NV04_PFB_BOOT_0, ~0, + NV04_PFB_BOOT_0_RAM_AMOUNT_16MB | + NV04_PFB_BOOT_0_RAM_WIDTH_128 | + NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT); + + for (i = 0; i < 4; i++) + fbmem_poke(fb, 4 * i, patt); + + fbmem_poke(fb, 0x400000, patt + 1); + + if (fbmem_peek(fb, 0) == patt + 1) { + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_TYPE, + NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT); + nvkm_mask(device, NV04_PFB_DEBUG_0, + NV04_PFB_DEBUG_0_REFRESH_OFF, 0); + + for (i = 0; i < 4; i++) + fbmem_poke(fb, 4 * i, patt); + + if ((fbmem_peek(fb, 0xc) & 0xffff) != (patt & 0xffff)) + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_WIDTH_128 | + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + } else + if ((fbmem_peek(fb, 0xc) & 0xffff0000) != (patt & 0xffff0000)) { + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_WIDTH_128 | + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + } else + if (fbmem_peek(fb, 0) != patt) { + if (fbmem_readback(fb, 0x800000, patt)) + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + else + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_TYPE, + NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT); + } else + if (!fbmem_readback(fb, 0x800000, patt)) { + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + + } + + /* Refresh on, sequencer on */ + nvkm_mask(device, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0); + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) & ~0x20); + fbmem_fini(fb); +} + +static int +powerctrl_1_shift(int chip_version, int reg) +{ + int shift = -4; + + if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20) + return shift; + + switch (reg) { + case 0x680520: + shift += 4; fallthrough; + case 0x680508: + shift += 4; fallthrough; + case 0x680504: + shift += 4; fallthrough; + case 0x680500: + shift += 4; + } + + /* + * the shift for vpll regs is only used for nv3x chips with a single + * stage pll + */ + if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 || + chip_version == 0x36 || chip_version >= 0x40)) + shift = -4; + + return shift; +} + +void +setPLL_single(struct nvkm_devinit *init, u32 reg, + struct nvkm_pll_vals *pv) +{ + struct nvkm_device *device = init->subdev.device; + int chip_version = device->bios->version.chip; + uint32_t oldpll = nvkm_rd32(device, reg); + int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff; + uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1; + uint32_t saved_powerctrl_1 = 0; + int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg); + + if (oldpll == pll) + return; /* already set */ + + if (shift_powerctrl_1 >= 0) { + saved_powerctrl_1 = nvkm_rd32(device, 0x001584); + nvkm_wr32(device, 0x001584, + (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | + 1 << shift_powerctrl_1); + } + + if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1)) + /* upclock -- write new post divider first */ + nvkm_wr32(device, reg, pv->log2P << 16 | (oldpll & 0xffff)); + else + /* downclock -- write new NM first */ + nvkm_wr32(device, reg, (oldpll & 0xffff0000) | pv->NM1); + + if ((chip_version < 0x17 || chip_version == 0x1a) && + chip_version != 0x11) + /* wait a bit on older chips */ + msleep(64); + nvkm_rd32(device, reg); + + /* then write the other half as well */ + nvkm_wr32(device, reg, pll); + + if (shift_powerctrl_1 >= 0) + nvkm_wr32(device, 0x001584, saved_powerctrl_1); +} + +static uint32_t +new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580) +{ + bool head_a = (reg1 == 0x680508); + + if (ss) /* single stage pll mode */ + ramdac580 |= head_a ? 0x00000100 : 0x10000000; + else + ramdac580 &= head_a ? 0xfffffeff : 0xefffffff; + + return ramdac580; +} + +void +setPLL_double_highregs(struct nvkm_devinit *init, u32 reg1, + struct nvkm_pll_vals *pv) +{ + struct nvkm_device *device = init->subdev.device; + int chip_version = device->bios->version.chip; + bool nv3035 = chip_version == 0x30 || chip_version == 0x35; + uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70); + uint32_t oldpll1 = nvkm_rd32(device, reg1); + uint32_t oldpll2 = !nv3035 ? nvkm_rd32(device, reg2) : 0; + uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1; + uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2; + uint32_t oldramdac580 = 0, ramdac580 = 0; + bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */ + uint32_t saved_powerctrl_1 = 0, savedc040 = 0; + int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1); + + /* model specific additions to generic pll1 and pll2 set up above */ + if (nv3035) { + pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 | + (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4; + pll2 = 0; + } + if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */ + oldramdac580 = nvkm_rd32(device, 0x680580); + ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580); + if (oldramdac580 != ramdac580) + oldpll1 = ~0; /* force mismatch */ + if (single_stage) + /* magic value used by nvidia in single stage mode */ + pll2 |= 0x011f; + } + if (chip_version > 0x70) + /* magic bits set by the blob (but not the bios) on g71-73 */ + pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28; + + if (oldpll1 == pll1 && oldpll2 == pll2) + return; /* already set */ + + if (shift_powerctrl_1 >= 0) { + saved_powerctrl_1 = nvkm_rd32(device, 0x001584); + nvkm_wr32(device, 0x001584, + (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | + 1 << shift_powerctrl_1); + } + + if (chip_version >= 0x40) { + int shift_c040 = 14; + + switch (reg1) { + case 0x680504: + shift_c040 += 2; fallthrough; + case 0x680500: + shift_c040 += 2; fallthrough; + case 0x680520: + shift_c040 += 2; fallthrough; + case 0x680508: + shift_c040 += 2; + } + + savedc040 = nvkm_rd32(device, 0xc040); + if (shift_c040 != 14) + nvkm_wr32(device, 0xc040, savedc040 & ~(3 << shift_c040)); + } + + if (oldramdac580 != ramdac580) + nvkm_wr32(device, 0x680580, ramdac580); + + if (!nv3035) + nvkm_wr32(device, reg2, pll2); + nvkm_wr32(device, reg1, pll1); + + if (shift_powerctrl_1 >= 0) + nvkm_wr32(device, 0x001584, saved_powerctrl_1); + if (chip_version >= 0x40) + nvkm_wr32(device, 0xc040, savedc040); +} + +void +setPLL_double_lowregs(struct nvkm_devinit *init, u32 NMNMreg, + struct nvkm_pll_vals *pv) +{ + /* When setting PLLs, there is a merry game of disabling and enabling + * various bits of hardware during the process. This function is a + * synthesis of six nv4x traces, nearly each card doing a subtly + * different thing. With luck all the necessary bits for each card are + * combined herein. Without luck it deviates from each card's formula + * so as to not work on any :) + */ + struct nvkm_device *device = init->subdev.device; + uint32_t Preg = NMNMreg - 4; + bool mpll = Preg == 0x4020; + uint32_t oldPval = nvkm_rd32(device, Preg); + uint32_t NMNM = pv->NM2 << 16 | pv->NM1; + uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) | + 0xc << 28 | pv->log2P << 16; + uint32_t saved4600 = 0; + /* some cards have different maskc040s */ + uint32_t maskc040 = ~(3 << 14), savedc040; + bool single_stage = !pv->NM2 || pv->N2 == pv->M2; + + if (nvkm_rd32(device, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval) + return; + + if (Preg == 0x4000) + maskc040 = ~0x333; + if (Preg == 0x4058) + maskc040 = ~(0xc << 24); + + if (mpll) { + struct nvbios_pll info; + uint8_t Pval2; + + if (nvbios_pll_parse(device->bios, Preg, &info)) + return; + + Pval2 = pv->log2P + info.bias_p; + if (Pval2 > info.max_p) + Pval2 = info.max_p; + Pval |= 1 << 28 | Pval2 << 20; + + saved4600 = nvkm_rd32(device, 0x4600); + nvkm_wr32(device, 0x4600, saved4600 | 8 << 28); + } + if (single_stage) + Pval |= mpll ? 1 << 12 : 1 << 8; + + nvkm_wr32(device, Preg, oldPval | 1 << 28); + nvkm_wr32(device, Preg, Pval & ~(4 << 28)); + if (mpll) { + Pval |= 8 << 20; + nvkm_wr32(device, 0x4020, Pval & ~(0xc << 28)); + nvkm_wr32(device, 0x4038, Pval & ~(0xc << 28)); + } + + savedc040 = nvkm_rd32(device, 0xc040); + nvkm_wr32(device, 0xc040, savedc040 & maskc040); + + nvkm_wr32(device, NMNMreg, NMNM); + if (NMNMreg == 0x4024) + nvkm_wr32(device, 0x403c, NMNM); + + nvkm_wr32(device, Preg, Pval); + if (mpll) { + Pval &= ~(8 << 20); + nvkm_wr32(device, 0x4020, Pval); + nvkm_wr32(device, 0x4038, Pval); + nvkm_wr32(device, 0x4600, saved4600); + } + + nvkm_wr32(device, 0xc040, savedc040); + + if (mpll) { + nvkm_wr32(device, 0x4020, Pval & ~(1 << 28)); + nvkm_wr32(device, 0x4038, Pval & ~(1 << 28)); + } +} + +int +nv04_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &devinit->subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvkm_pll_vals pv; + struct nvbios_pll info; + int cv = bios->version.chip; + int N1, M1, N2, M2, P; + int ret; + + ret = nvbios_pll_parse(bios, type > 0x405c ? type : type - 4, &info); + if (ret) + return ret; + + ret = nv04_pll_calc(subdev, &info, freq, &N1, &M1, &N2, &M2, &P); + if (!ret) + return -EINVAL; + + pv.refclk = info.refclk; + pv.N1 = N1; + pv.M1 = M1; + pv.N2 = N2; + pv.M2 = M2; + pv.log2P = P; + + if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || + cv >= 0x40) { + if (type > 0x405c) + setPLL_double_highregs(devinit, type, &pv); + else + setPLL_double_lowregs(devinit, type, &pv); + } else + setPLL_single(devinit, type, &pv); + + return 0; +} + +int +nv04_devinit_post(struct nvkm_devinit *init, bool execute) +{ + return nvbios_post(&init->subdev, execute); +} + +void +nv04_devinit_preinit(struct nvkm_devinit *base) +{ + struct nv04_devinit *init = nv04_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + + /* make i2c busses accessible */ + nvkm_mask(device, 0x000200, 0x00000001, 0x00000001); + + /* unslave crtcs */ + if (init->owner < 0) + init->owner = nvkm_rdvgaowner(device); + nvkm_wrvgaowner(device, 0); + + if (!init->base.post) { + u32 htotal = nvkm_rdvgac(device, 0, 0x06); + htotal |= (nvkm_rdvgac(device, 0, 0x07) & 0x01) << 8; + htotal |= (nvkm_rdvgac(device, 0, 0x07) & 0x20) << 4; + htotal |= (nvkm_rdvgac(device, 0, 0x25) & 0x01) << 10; + htotal |= (nvkm_rdvgac(device, 0, 0x41) & 0x01) << 11; + if (!htotal) { + nvkm_debug(subdev, "adaptor not initialised\n"); + init->base.post = true; + } + } +} + +void * +nv04_devinit_dtor(struct nvkm_devinit *base) +{ + struct nv04_devinit *init = nv04_devinit(base); + /* restore vga owner saved at first init */ + nvkm_wrvgaowner(init->base.subdev.device, init->owner); + return init; +} + +int +nv04_devinit_new_(const struct nvkm_devinit_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_devinit **pinit) +{ + struct nv04_devinit *init; + + if (!(init = kzalloc(sizeof(*init), GFP_KERNEL))) + return -ENOMEM; + *pinit = &init->base; + + nvkm_devinit_ctor(func, device, type, inst, &init->base); + init->owner = -1; + return 0; +} + +static const struct nvkm_devinit_func +nv04_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv04_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv04_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv04_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h new file mode 100644 index 000000000..06ad8a606 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV04_DEVINIT_H__ +#define __NV04_DEVINIT_H__ +#define nv04_devinit(p) container_of((p), struct nv04_devinit, base) +#include "priv.h" +struct nvkm_pll_vals; + +struct nv04_devinit { + struct nvkm_devinit base; + int owner; +}; + +int nv04_devinit_new_(const struct nvkm_devinit_func *, struct nvkm_device *, + enum nvkm_subdev_type, int, struct nvkm_devinit **); +void *nv04_devinit_dtor(struct nvkm_devinit *); +void nv04_devinit_preinit(struct nvkm_devinit *); +void nv04_devinit_fini(struct nvkm_devinit *); +int nv04_devinit_pll_set(struct nvkm_devinit *, u32, u32); + +void setPLL_single(struct nvkm_devinit *, u32, struct nvkm_pll_vals *); +void setPLL_double_highregs(struct nvkm_devinit *, u32, struct nvkm_pll_vals *); +void setPLL_double_lowregs(struct nvkm_devinit *, u32, struct nvkm_pll_vals *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c new file mode 100644 index 000000000..1410befd2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c @@ -0,0 +1,143 @@ +/* + * 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 "fbmem.h" + +#include <subdev/bios.h> +#include <subdev/bios/bmp.h> +#include <subdev/bios/init.h> +#include <subdev/vga.h> + +static void +nv05_devinit_meminit(struct nvkm_devinit *init) +{ + static const u8 default_config_tab[][2] = { + { 0x24, 0x00 }, + { 0x28, 0x00 }, + { 0x24, 0x01 }, + { 0x1f, 0x00 }, + { 0x0f, 0x00 }, + { 0x17, 0x00 }, + { 0x06, 0x00 }, + { 0x00, 0x00 } + }; + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct io_mapping *fb; + u32 patt = 0xdeadbeef; + u16 data; + u8 strap, ramcfg[2]; + int i, v; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + strap = (nvkm_rd32(device, 0x101000) & 0x0000003c) >> 2; + if ((data = bmp_mem_init_table(bios))) { + ramcfg[0] = nvbios_rd08(bios, data + 2 * strap + 0); + ramcfg[1] = nvbios_rd08(bios, data + 2 * strap + 1); + } else { + ramcfg[0] = default_config_tab[strap][0]; + ramcfg[1] = default_config_tab[strap][1]; + } + + /* Sequencer off */ + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) | 0x20); + + if (nvkm_rd32(device, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_UMA_ENABLE) + goto out; + + nvkm_mask(device, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0); + + /* If present load the hardcoded scrambling table */ + if (data) { + for (i = 0, data += 0x10; i < 8; i++, data += 4) { + u32 scramble = nvbios_rd32(bios, data); + nvkm_wr32(device, NV04_PFB_SCRAMBLE(i), scramble); + } + } + + /* Set memory type/width/length defaults depending on the straps */ + nvkm_mask(device, NV04_PFB_BOOT_0, 0x3f, ramcfg[0]); + + if (ramcfg[1] & 0x80) + nvkm_mask(device, NV04_PFB_CFG0, 0, NV04_PFB_CFG0_SCRAMBLE); + + nvkm_mask(device, NV04_PFB_CFG1, 0x700001, (ramcfg[1] & 1) << 20); + nvkm_mask(device, NV04_PFB_CFG1, 0, 1); + + /* Probe memory bus width */ + for (i = 0; i < 4; i++) + fbmem_poke(fb, 4 * i, patt); + + if (fbmem_peek(fb, 0xc) != patt) + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_WIDTH_128, 0); + + /* Probe memory length */ + v = nvkm_rd32(device, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_RAM_AMOUNT; + + if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_32MB && + (!fbmem_readback(fb, 0x1000000, ++patt) || + !fbmem_readback(fb, 0, ++patt))) + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_16MB); + + if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_16MB && + !fbmem_readback(fb, 0x800000, ++patt)) + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + + if (!fbmem_readback(fb, 0x400000, ++patt)) + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + +out: + /* Sequencer on */ + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) & ~0x20); + fbmem_fini(fb); +} + +static const struct nvkm_devinit_func +nv05_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv05_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv05_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv05_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c new file mode 100644 index 000000000..a6aa8786d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c @@ -0,0 +1,113 @@ +/* + * 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 "fbmem.h" + +#include <subdev/bios.h> +#include <subdev/bios/init.h> + +static void +nv10_devinit_meminit(struct nvkm_devinit *init) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + static const int mem_width[] = { 0x10, 0x00, 0x20 }; + int mem_width_count; + uint32_t patt = 0xdeadbeef; + struct io_mapping *fb; + int i, j, k; + + if (device->card_type >= NV_11 && device->chipset >= 0x17) + mem_width_count = 3; + else + mem_width_count = 2; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + nvkm_wr32(device, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); + + /* Probe memory bus width */ + for (i = 0; i < mem_width_count; i++) { + nvkm_mask(device, NV04_PFB_CFG0, 0x30, mem_width[i]); + + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) + fbmem_poke(fb, 0x1c, 0); + + fbmem_poke(fb, 0x1c, patt); + fbmem_poke(fb, 0x3c, 0); + + if (fbmem_peek(fb, 0x1c) == patt) + goto mem_width_found; + } + } + +mem_width_found: + patt <<= 1; + + /* Probe amount of installed memory */ + for (i = 0; i < 4; i++) { + int off = nvkm_rd32(device, 0x10020c) - 0x100000; + + fbmem_poke(fb, off, patt); + fbmem_poke(fb, 0, 0); + + fbmem_peek(fb, 0); + fbmem_peek(fb, 0); + fbmem_peek(fb, 0); + fbmem_peek(fb, 0); + + if (fbmem_peek(fb, off) == patt) + goto amount_found; + } + + /* IC missing - disable the upper half memory space. */ + nvkm_mask(device, NV04_PFB_CFG0, 0x1000, 0); + +amount_found: + fbmem_fini(fb); +} + +static const struct nvkm_devinit_func +nv10_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv10_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv10_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv10_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c new file mode 100644 index 000000000..4cc5ef9a5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c @@ -0,0 +1,42 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/init.h> + +static const struct nvkm_devinit_func +nv1a_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv1a_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv1a_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c new file mode 100644 index 000000000..67f46df72 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c @@ -0,0 +1,79 @@ +/* + * 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 "fbmem.h" + +#include <subdev/bios.h> +#include <subdev/bios/init.h> + +static void +nv20_devinit_meminit(struct nvkm_devinit *init) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + uint32_t mask = (device->chipset >= 0x25 ? 0x300 : 0x900); + uint32_t amount, off; + struct io_mapping *fb; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + nvkm_wr32(device, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); + + /* Allow full addressing */ + nvkm_mask(device, NV04_PFB_CFG0, 0, mask); + + amount = nvkm_rd32(device, 0x10020c); + for (off = amount; off > 0x2000000; off -= 0x2000000) + fbmem_poke(fb, off - 4, off); + + amount = nvkm_rd32(device, 0x10020c); + if (amount != fbmem_peek(fb, amount - 4)) + /* IC missing - disable the upper half memory space. */ + nvkm_mask(device, NV04_PFB_CFG0, mask, 0); + + fbmem_fini(fb); +} + +static const struct nvkm_devinit_func +nv20_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv20_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv20_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv20_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c new file mode 100644 index 000000000..380995d39 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c @@ -0,0 +1,178 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/disp.h> +#include <subdev/bios/init.h> +#include <subdev/bios/pll.h> +#include <subdev/clk/pll.h> +#include <subdev/vga.h> + +int +nv50_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvbios_pll info; + int N1, M1, N2, M2, P; + int ret; + + ret = nvbios_pll_parse(bios, type, &info); + if (ret) { + nvkm_error(subdev, "failed to retrieve pll data, %d\n", ret); + return ret; + } + + ret = nv04_pll_calc(subdev, &info, freq, &N1, &M1, &N2, &M2, &P); + if (!ret) { + nvkm_error(subdev, "failed pll calculation\n"); + return -EINVAL; + } + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nvkm_wr32(device, info.reg + 0, 0x10000611); + nvkm_mask(device, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1); + nvkm_mask(device, info.reg + 8, 0x7fff00ff, (P << 28) | + (M2 << 16) | N2); + break; + case PLL_MEMORY: + nvkm_mask(device, info.reg + 0, 0x01ff0000, + (P << 22) | + (info.bias_p << 19) | + (P << 16)); + nvkm_wr32(device, info.reg + 4, (N1 << 8) | M1); + break; + default: + nvkm_mask(device, info.reg + 0, 0x00070000, (P << 16)); + nvkm_wr32(device, info.reg + 4, (N1 << 8) | M1); + break; + } + + return 0; +} + +static u64 +nv50_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) + nvkm_subdev_disable(device, NVKM_ENGINE_MPEG, 0); + + return disable; +} + +void +nv50_devinit_preinit(struct nvkm_devinit *base) +{ + struct nvkm_subdev *subdev = &base->subdev; + struct nvkm_device *device = subdev->device; + + /* our heuristics can't detect whether the board has had its + * devinit scripts executed or not if the display engine is + * missing, assume it's a secondary gpu which requires post + */ + if (!base->post) { + nvkm_devinit_disable(base); + if (!device->disp) + base->post = true; + } + + /* magic to detect whether or not x86 vbios code has executed + * the devinit scripts to initialise the board + */ + if (!base->post) { + if (!nvkm_rdvgac(device, 0, 0x00) && + !nvkm_rdvgac(device, 0, 0x1a)) { + nvkm_debug(subdev, "adaptor not initialised\n"); + base->post = true; + } + } +} + +void +nv50_devinit_init(struct nvkm_devinit *base) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvbios_outp info; + struct dcb_output outp; + u8 ver = 0xff, hdr, cnt, len; + int i = 0; + + /* if we ran the init tables, we have to execute the first script + * pointer of each dcb entry's display encoder table in order + * to properly initialise each encoder. + */ + while (init->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { + if (nvbios_outp_match(bios, outp.hasht, outp.hashm, + &ver, &hdr, &cnt, &len, &info)) { + nvbios_init(subdev, info.script[0], + init.outp = &outp; + init.or = ffs(outp.or) - 1; + init.link = outp.sorconf.link == 2; + ); + } + i++; + } +} + +int +nv50_devinit_new_(const struct nvkm_devinit_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_devinit **pinit) +{ + struct nv50_devinit *init; + + if (!(init = kzalloc(sizeof(*init), GFP_KERNEL))) + return -ENOMEM; + *pinit = &init->base; + + nvkm_devinit_ctor(func, device, type, inst, &init->base); + return 0; +} + +static const struct nvkm_devinit_func +nv50_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = nv50_devinit_pll_set, + .disable = nv50_devinit_disable, +}; + +int +nv50_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&nv50_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h new file mode 100644 index 000000000..987a7f478 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_DEVINIT_H__ +#define __NV50_DEVINIT_H__ +#define nv50_devinit(p) container_of((p), struct nv50_devinit, base) +#include "priv.h" + +struct nv50_devinit { + struct nvkm_devinit base; + u32 r001540; +}; + +int nv50_devinit_new_(const struct nvkm_devinit_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_devinit **); +void nv50_devinit_preinit(struct nvkm_devinit *); +void nv50_devinit_init(struct nvkm_devinit *); +int nv50_devinit_pll_set(struct nvkm_devinit *, u32, u32); + +int gt215_devinit_pll_set(struct nvkm_devinit *, u32, u32); + +int gf100_devinit_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +int gf100_devinit_pll_set(struct nvkm_devinit *, u32, u32); +void gf100_devinit_preinit(struct nvkm_devinit *); + +u64 gm107_devinit_disable(struct nvkm_devinit *); + +int gm200_devinit_post(struct nvkm_devinit *, bool); +void gm200_devinit_preos(struct nv50_devinit *, bool); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h new file mode 100644 index 000000000..dd8b038a8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DEVINIT_PRIV_H__ +#define __NVKM_DEVINIT_PRIV_H__ +#define nvkm_devinit(p) container_of((p), struct nvkm_devinit, subdev) +#include <subdev/devinit.h> + +struct nvkm_devinit_func { + void *(*dtor)(struct nvkm_devinit *); + void (*preinit)(struct nvkm_devinit *); + void (*init)(struct nvkm_devinit *); + int (*post)(struct nvkm_devinit *, bool post); + u32 (*mmio)(struct nvkm_devinit *, u32); + void (*meminit)(struct nvkm_devinit *); + int (*pll_set)(struct nvkm_devinit *, u32 type, u32 freq); + u64 (*disable)(struct nvkm_devinit *); +}; + +void nvkm_devinit_ctor(const struct nvkm_devinit_func *, struct nvkm_device *, + enum nvkm_subdev_type, int inst, struct nvkm_devinit *); +u64 nvkm_devinit_disable(struct nvkm_devinit *); + +int nv04_devinit_post(struct nvkm_devinit *, bool); +int tu102_devinit_post(struct nvkm_devinit *, bool); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c new file mode 100644 index 000000000..81a1ad2c8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c @@ -0,0 +1,112 @@ +/* + * Copyright 2018 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. + */ +#include "nv50.h" + +#include <subdev/bios.h> +#include <subdev/bios/pll.h> +#include <subdev/clk/pll.h> + +static int +tu102_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int head = type - PLL_VPLL0; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_wr32(device, 0x00ef10 + (head * 0x40), fN << 16); + nvkm_wr32(device, 0x00ef04 + (head * 0x40), (P << 16) | + (N << 8) | + (M << 0)); + /*XXX*/ + nvkm_wr32(device, 0x00ef0c + (head * 0x40), 0x00000900); + nvkm_wr32(device, 0x00ef00 + (head * 0x40), 0x02000014); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +tu102_devinit_wait(struct nvkm_device *device) +{ + unsigned timeout = 50 + 2000; + + do { + if (nvkm_rd32(device, 0x118128) & 0x00000001) { + if ((nvkm_rd32(device, 0x118234) & 0x000000ff) == 0xff) + return 0; + } + + usleep_range(1000, 2000); + } while (timeout--); + + return -ETIMEDOUT; +} + +int +tu102_devinit_post(struct nvkm_devinit *base, bool post) +{ + struct nv50_devinit *init = nv50_devinit(base); + int ret; + + ret = tu102_devinit_wait(init->base.subdev.device); + if (ret) + return ret; + + gm200_devinit_preos(init, post); + return 0; +} + +static const struct nvkm_devinit_func +tu102_devinit = { + .init = nv50_devinit_init, + .post = tu102_devinit_post, + .pll_set = tu102_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +tu102_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&tu102_devinit, device, type, inst, pinit); +} |