diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2013-03-04 05:01:37 (GMT) |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2013-11-08 05:40:19 (GMT) |
commit | aae95ca708140307813e49af6d0d4a7205509129 (patch) | |
tree | 20acced9f1a88e7be22f8f1c85c8bc610a2932b1 /drivers | |
parent | 26fdd78cce3f51a49e1f2d3ad27ee893a28d220e (diff) | |
download | linux-fsl-qoriq-aae95ca708140307813e49af6d0d4a7205509129.tar.xz |
drm/nouveau/fb: implement various bits of work towards memory reclocking
Not even remotely ready for the vast majority of the world.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers')
21 files changed, 3093 insertions, 60 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 64b4691..edcf801 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -94,6 +94,7 @@ nouveau-y += core/subdev/fb/nva3.o nouveau-y += core/subdev/fb/nvaa.o nouveau-y += core/subdev/fb/nvaf.o nouveau-y += core/subdev/fb/nvc0.o +nouveau-y += core/subdev/fb/nve0.o nouveau-y += core/subdev/fb/ramnv04.o nouveau-y += core/subdev/fb/ramnv10.o nouveau-y += core/subdev/fb/ramnv1a.o @@ -107,6 +108,9 @@ nouveau-y += core/subdev/fb/ramnv50.o nouveau-y += core/subdev/fb/ramnva3.o nouveau-y += core/subdev/fb/ramnvaa.o nouveau-y += core/subdev/fb/ramnvc0.o +nouveau-y += core/subdev/fb/ramnve0.o +nouveau-y += core/subdev/fb/sddr3.o +nouveau-y += core/subdev/fb/gddr5.o nouveau-y += core/subdev/gpio/base.o nouveau-y += core/subdev/gpio/nv10.o nouveau-y += core/subdev/gpio/nv50.o diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 33f3c92..3900104 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -69,7 +69,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -102,7 +102,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -135,7 +135,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -168,7 +168,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -203,7 +203,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h index 33cae48..8541aa3 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h @@ -99,6 +99,7 @@ extern struct nouveau_oclass *nva3_fb_oclass; extern struct nouveau_oclass *nvaa_fb_oclass; extern struct nouveau_oclass *nvaf_fb_oclass; extern struct nouveau_oclass *nvc0_fb_oclass; +extern struct nouveau_oclass *nve0_fb_oclass; struct nouveau_ram { struct nouveau_object base; @@ -125,9 +126,17 @@ struct nouveau_ram { int (*get)(struct nouveau_fb *, u64 size, u32 align, u32 size_nc, u32 type, struct nouveau_mem **); void (*put)(struct nouveau_fb *, struct nouveau_mem **); + int (*calc)(struct nouveau_fb *, u32 freq); int (*prog)(struct nouveau_fb *); void (*tidy)(struct nouveau_fb *); + struct { + u8 version; + u32 data; + u8 size; + } rammap, ramcfg, timing; + u32 freq; + u32 mr[16]; }; #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c index 2fe1f71..8eca457 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c @@ -45,6 +45,7 @@ nva3_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, lM = max(lM, (int)info->vco1.min_m); hM = (info->refclk + info->vco1.min_inputfreq) / info->vco1.min_inputfreq; hM = min(hM, (int)info->vco1.max_m); + lM = min(lM, hM); for (M = lM; M <= hM; M++) { u32 tmp = freq * *P * M; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c new file mode 100644 index 0000000..34f9605 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c @@ -0,0 +1,96 @@ +/* + * 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 <bskeggs@redhat.com> + */ + +#include <subdev/bios.h> +#include "priv.h" + +int +nouveau_gddr5_calc(struct nouveau_ram *ram) +{ + struct nouveau_bios *bios = nouveau_bios(ram); + int pd, lf, xd, vh, vr, vo; + int WL, CL, WR, at, dt, ds; + int rq = ram->freq < 1000000; /* XXX */ + + switch (!!ram->ramcfg.data * ram->ramcfg.version) { + case 0x11: + pd = (nv_ro08(bios, ram->ramcfg.data + 0x01) & 0x80) >> 7; + lf = (nv_ro08(bios, ram->ramcfg.data + 0x01) & 0x40) >> 6; + xd = !(nv_ro08(bios, ram->ramcfg.data + 0x01) & 0x20); + vh = (nv_ro08(bios, ram->ramcfg.data + 0x02) & 0x10) >> 4; + vr = (nv_ro08(bios, ram->ramcfg.data + 0x02) & 0x04) >> 2; + vo = nv_ro08(bios, ram->ramcfg.data + 0x06) & 0xff; + break; + default: + return -ENOSYS; + } + + switch (!!ram->timing.data * ram->timing.version) { + case 0x20: + WL = (nv_ro16(bios, ram->timing.data + 0x04) & 0x0f80) >> 7; + CL = nv_ro08(bios, ram->timing.data + 0x04) & 0x1f; + WR = nv_ro08(bios, ram->timing.data + 0x0a) & 0x7f; + at = (nv_ro08(bios, ram->timing.data + 0x2e) & 0xc0) >> 6; + dt = nv_ro08(bios, ram->timing.data + 0x2e) & 0x03; + ds = nv_ro08(bios, ram->timing.data + 0x2f) & 0x03; + 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 & 0x03) << 4; + ram->mr[1] |= (dt & 0x03) << 2; + ram->mr[1] |= (ds & 0x03) << 0; + + ram->mr[3] &= ~0x020; + ram->mr[3] |= (rq & 0x01) << 5; + + 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 (!(ram->mr[7] & 0x100)) + vr = 0; /* binary driver does this.. bug? */ + ram->mr[7] &= ~0x188; + ram->mr[7] |= (vr & 0x01) << 8; + ram->mr[7] |= (vh & 0x01) << 7; + ram->mr[7] |= (lf & 0x01) << 3; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.h b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.h new file mode 100644 index 0000000..581f808 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.h @@ -0,0 +1,17 @@ +#ifndef __NVKM_FB_NV40_H__ +#define __NVKM_FB_NV40_H__ + +#include "priv.h" + +struct nv40_ram { + struct nouveau_ram base; + u32 ctrl; + u32 coef; +}; + + +int nv40_ram_calc(struct nouveau_fb *, u32); +int nv40_ram_prog(struct nouveau_fb *); +void nv40_ram_tidy(struct nouveau_fb *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c index 7fa6a91..e5fc37c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c @@ -22,24 +22,18 @@ * Authors: Ben Skeggs */ -#include "nv04.h" - -struct nvc0_fb_priv { - struct nouveau_fb base; - struct page *r100c10_page; - dma_addr_t r100c10; -}; +#include "nvc0.h" extern const u8 nvc0_pte_storage_type_map[256]; -static bool +bool nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags) { u8 memtype = (tile_flags & 0x0000ff00) >> 8; return likely((nvc0_pte_storage_type_map[memtype] != 0xff)); } -static int +int nvc0_fb_init(struct nouveau_object *object) { struct nvc0_fb_priv *priv = (void *)object; @@ -54,7 +48,7 @@ nvc0_fb_init(struct nouveau_object *object) return 0; } -static void +void nvc0_fb_dtor(struct nouveau_object *object) { struct nouveau_device *device = nv_device(object); @@ -69,7 +63,7 @@ nvc0_fb_dtor(struct nouveau_object *object) nouveau_fb_destroy(&priv->base); } -static int +int nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h new file mode 100644 index 0000000..9e1931e --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h @@ -0,0 +1,29 @@ +#ifndef __NVKM_RAM_NVC0_H__ +#define __NVKM_RAM_NVC0_H__ + +#include "priv.h" +#include "nv50.h" + +struct nvc0_fb_priv { + struct nouveau_fb base; + struct page *r100c10_page; + dma_addr_t r100c10; +}; + +int nvc0_fb_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void nvc0_fb_dtor(struct nouveau_object *); +int nvc0_fb_init(struct nouveau_object *); +bool nvc0_fb_memtype_valid(struct nouveau_fb *, u32); + + +#define nvc0_ram_create(p,e,o,d) \ + nvc0_ram_create_((p), (e), (o), sizeof(**d), (void **)d) +int nvc0_ram_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, void **); +int nvc0_ram_get(struct nouveau_fb *, u64, u32, u32, u32, + struct nouveau_mem **); +void nvc0_ram_put(struct nouveau_fb *, struct nouveau_mem **); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nve0.c new file mode 100644 index 0000000..595db50 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nve0.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 "nvc0.h" + +struct nouveau_oclass * +nve0_fb_oclass = &(struct nouveau_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xe0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_fb_ctor, + .dtor = nvc0_fb_dtor, + .init = nvc0_fb_init, + .fini = _nouveau_fb_fini, + }, + .memtype = nvc0_fb_memtype_valid, + .ram = &nve0_ram_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h index eaf3795..4931252 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h @@ -31,6 +31,10 @@ extern struct nouveau_oclass nv50_ram_oclass; extern struct nouveau_oclass nva3_ram_oclass; extern struct nouveau_oclass nvaa_ram_oclass; extern struct nouveau_oclass nvc0_ram_oclass; +extern struct nouveau_oclass nve0_ram_oclass; + +int nouveau_sddr3_calc(struct nouveau_ram *ram); +int nouveau_gddr5_calc(struct nouveau_ram *ram); #define nouveau_fb_create(p,e,c,d) \ nouveau_fb_create_((p), (e), (c), sizeof(**d), (void **)d) diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h new file mode 100644 index 0000000..0f57fcf --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h @@ -0,0 +1,118 @@ +#ifndef __NVKM_FBRAM_FUC_H__ +#define __NVKM_FBRAM_FUC_H__ + +#include <subdev/pwr.h> + +struct ramfuc { + struct nouveau_memx *memx; + struct nouveau_fb *pfb; + int sequence; +}; + +struct ramfuc_reg { + int sequence; + bool force; + u32 addr[2]; + u32 data; +}; + +static inline struct ramfuc_reg +ramfuc_reg2(u32 addr1, u32 addr2) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = { addr1, addr2 }, + .data = 0xdeadbeef, + }; +} + +static inline struct ramfuc_reg +ramfuc_reg(u32 addr) +{ + return ramfuc_reg2(addr, addr); +} + +static inline int +ramfuc_init(struct ramfuc *ram, struct nouveau_fb *pfb) +{ + struct nouveau_pwr *ppwr = nouveau_pwr(pfb); + int ret; + + ret = nouveau_memx_init(ppwr, &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 = nouveau_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[0]); + return reg->data; +} + +static inline void +ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data) +{ + reg->sequence = ram->sequence; + reg->data = data; + if (reg->addr[0] != reg->addr[1]) + nouveau_memx_wr32(ram->memx, reg->addr[1], reg->data); + nouveau_memx_wr32(ram->memx, reg->addr[0], reg->data); +} + +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) +{ + nouveau_memx_wait(ram->memx, addr, mask, data, nsec); +} + +static inline void +ramfuc_nsec(struct ramfuc *ram, u32 nsec) +{ + nouveau_memx_nsec(ram->memx, nsec); +} + +#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)) + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c index ee49ac4..7648beb 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c @@ -22,7 +22,154 @@ * Authors: Ben Skeggs */ -#include "priv.h" +#include <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/pll.h> +#include <subdev/bios/init.h> +#include <subdev/clock.h> +#include <subdev/clock/pll.h> +#include <subdev/timer.h> + +#include <engine/fifo.h> + +#include "nv40.h" + +int +nv40_ram_calc(struct nouveau_fb *pfb, u32 freq) +{ + struct nouveau_bios *bios = nouveau_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 nouveau_fb *pfb) +{ + struct nouveau_bios *bios = nouveau_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 nouveau_fb *pfb) +{ +} static int nv40_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, @@ -30,7 +177,7 @@ nv40_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nouveau_fb *pfb = nouveau_fb(parent); - struct nouveau_ram *ram; + struct nv40_ram *ram; u32 pbus1218 = nv_rd32(pfb, 0x001218); int ret; @@ -40,15 +187,18 @@ nv40_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, 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_DDR2; break; + 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->size = nv_rd32(pfb, 0x10020c) & 0xff000000; - ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - ram->tags = nv_rd32(pfb, 0x100320); + 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; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c index 1dab7e1..d64498a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#include "priv.h" +#include "nv40.h" static int nv41_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, @@ -30,7 +30,7 @@ nv41_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nouveau_fb *pfb = nouveau_fb(parent); - struct nouveau_ram *ram; + struct nv40_ram *ram; u32 pfb474 = nv_rd32(pfb, 0x100474); int ret; @@ -40,15 +40,18 @@ nv41_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, return ret; if (pfb474 & 0x00000004) - ram->type = NV_MEM_TYPE_GDDR3; + ram->base.type = NV_MEM_TYPE_GDDR3; if (pfb474 & 0x00000002) - ram->type = NV_MEM_TYPE_DDR2; + ram->base.type = NV_MEM_TYPE_DDR2; if (pfb474 & 0x00000001) - ram->type = NV_MEM_TYPE_DDR1; + ram->base.type = NV_MEM_TYPE_DDR1; - ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; - ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - ram->tags = nv_rd32(pfb, 0x100320); + 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; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c index 25fff84..089acac 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#include "priv.h" +#include "nv40.h" static int nv44_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, @@ -30,7 +30,7 @@ nv44_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nouveau_fb *pfb = nouveau_fb(parent); - struct nouveau_ram *ram; + struct nv40_ram *ram; u32 pfb474 = nv_rd32(pfb, 0x100474); int ret; @@ -40,13 +40,16 @@ nv44_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, return ret; if (pfb474 & 0x00000004) - ram->type = NV_MEM_TYPE_GDDR3; + ram->base.type = NV_MEM_TYPE_GDDR3; if (pfb474 & 0x00000002) - ram->type = NV_MEM_TYPE_DDR2; + ram->base.type = NV_MEM_TYPE_DDR2; if (pfb474 & 0x00000001) - ram->type = NV_MEM_TYPE_DDR1; + ram->base.type = NV_MEM_TYPE_DDR1; - ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + 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; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c index ab7ef0a..baa013a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#include "priv.h" +#include "nv40.h" static int nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, @@ -30,7 +30,7 @@ nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nouveau_fb *pfb = nouveau_fb(parent); - struct nouveau_ram *ram; + struct nv40_ram *ram; u32 pfb914 = nv_rd32(pfb, 0x100914); int ret; @@ -40,15 +40,18 @@ nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, return ret; switch (pfb914 & 0x00000003) { - case 0x00000000: ram->type = NV_MEM_TYPE_DDR1; break; - case 0x00000001: ram->type = NV_MEM_TYPE_DDR2; break; - case 0x00000002: ram->type = NV_MEM_TYPE_GDDR3; break; + 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->size = nv_rd32(pfb, 0x10020c) & 0xff000000; - ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - ram->tags = nv_rd32(pfb, 0x100320); + 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; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c index 2315a21..76762a1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c @@ -23,14 +23,216 @@ */ #include <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/pll.h> +#include <subdev/bios/perf.h> +#include <subdev/bios/timing.h> +#include <subdev/clock/pll.h> +#include <subdev/fb.h> + +#include <core/option.h> #include <core/mm.h> +#include "ramseq.h" + #include "nv50.h" +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 nouveau_ram base; + struct nv50_ramseq hwsq; }; +#define QFX5800NVA0 1 + +static int +nv50_ram_calc(struct nouveau_fb *pfb, u32 freq) +{ + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + struct nvbios_perfE perfE; + struct nvbios_pll mpll; + struct bit_entry M; + struct { + u32 data; + u8 size; + } ramcfg, timing; + u8 ver, hdr, cnt, strap; + u32 data; + 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 */ + if (bit_entry(bios, 'M', &M) || M.version != 1 || M.length < 5) { + nv_error(pfb, "invalid/missing memory table\n"); + return -EINVAL; + } + + strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2; + data = nv_ro16(bios, M.offset + 3); + if (data) + strap = nv_ro08(bios, data + strap); + + 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_timing_entry(bios, strap, &ver, &hdr); + 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 nouveau_fb *pfb) +{ + struct nouveau_device *device = nv_device(pfb); + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + + ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + return 0; +} + +static void +nv50_ram_tidy(struct nouveau_fb *pfb) +{ + struct nv50_ram *ram = (void *)pfb->ram; + struct nv50_ramseq *hwsq = &ram->hwsq; + ram_exec(hwsq, false); +} + void __nv50_ram_put(struct nouveau_fb *pfb, struct nouveau_mem *mem) { @@ -218,13 +420,54 @@ nv50_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nv50_ram *ram; - int ret; + 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; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c index b40b793..f6292cd 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c @@ -22,25 +22,417 @@ * Authors: Ben Skeggs */ +#include <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/pll.h> +#include <subdev/bios/rammap.h> +#include <subdev/bios/timing.h> + +#include <subdev/clock/nva3.h> +#include <subdev/clock/pll.h> + +#include <core/option.h> + +#include "ramfuc.h" + #include "nv50.h" +struct nva3_ramfuc { + struct ramfuc base; + 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_0x100200; + struct ramfuc_reg r_0x100210; + struct ramfuc_reg r_0x100220[9]; + 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_0x100714; + struct ramfuc_reg r_0x100718; + struct ramfuc_reg r_0x10071c; + struct ramfuc_reg r_0x100760; + struct ramfuc_reg r_0x1007a0; + struct ramfuc_reg r_0x1007e0; + struct ramfuc_reg r_0x10f804; + struct ramfuc_reg r_0x1110e0; + struct ramfuc_reg r_0x111100; + struct ramfuc_reg r_0x111104; + struct ramfuc_reg r_0x611200; + struct ramfuc_reg r_mr[4]; +}; + struct nva3_ram { struct nouveau_ram base; + struct nva3_ramfuc fuc; }; static int +nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) +{ + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nva3_ram *ram = (void *)pfb->ram; + struct nva3_ramfuc *fuc = &ram->fuc; + struct nva3_clock_info mclk; + struct bit_entry M; + u8 ver, cnt, strap; + u32 data; + struct { + u32 data; + u8 size; + } rammap, ramcfg, timing; + u32 r004018, r100760, ctrl; + u32 unk714, unk718, unk71c; + int ret; + + /* lookup memory config data relevant to the target frequency */ + rammap.data = nvbios_rammap_match(bios, freq / 1000, &ver, &rammap.size, + &cnt, &ramcfg.size); + 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 */ + if (bit_entry(bios, 'M', &M) || M.version != 2 || M.length < 3) { + nv_error(pfb, "invalid/missing memory table\n"); + return -EINVAL; + } + + strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2; + data = nv_ro16(bios, M.offset + 1); + if (data) + strap = nv_ro08(bios, data + strap); + + 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_timing_entry(bios, strap, &ver, + &timing.size); + if (!timing.data || ver != 0x10 || timing.size < 0x19) { + nv_error(pfb, "invalid/missing timing entry\n"); + return -EINVAL; + } + } else { + timing.data = 0; + } + + ret = nva3_clock_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk); + if (ret < 0) { + nv_error(pfb, "failed mclk calculation\n"); + return ret; + } + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + /* XXX: where the fuck does 750MHz come from? */ + if (freq <= 750000) { + r004018 = 0x10000000; + r100760 = 0x22222222; + } else { + r004018 = 0x00000000; + r100760 = 0x00000000; + } + + ctrl = ram_rd32(fuc, 0x004000); + if (ctrl & 0x00000008) { + if (mclk.pll) { + ram_mask(fuc, 0x004128, 0x00000101, 0x00000101); + ram_wr32(fuc, 0x004004, mclk.pll); + ram_wr32(fuc, 0x004000, (ctrl |= 0x00000001)); + ram_wr32(fuc, 0x004000, (ctrl &= 0xffffffef)); + ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000); + ram_wr32(fuc, 0x004000, (ctrl |= 0x00000010)); + ram_wr32(fuc, 0x004018, 0x00005000 | r004018); + ram_wr32(fuc, 0x004000, (ctrl |= 0x00000004)); + } + } else { + u32 ssel = 0x00000101; + if (mclk.clk) + ssel |= mclk.clk; + else + ssel |= 0x00080000; /* 324MHz, shouldn't matter... */ + ram_mask(fuc, 0x004168, 0x003f3141, ctrl); + } + + if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); + } else { + ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); + ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); + } + + if (!(nv_ro08(bios, rammap.data + 0x04) & 0x02)) + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x611200, 0x00003300); + if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) + ram_wr32(fuc, 0x111100, 0x4c020000); /*XXX*/ + + 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); + + ctrl = ram_rd32(fuc, 0x004000); + if (!(ctrl & 0x00000008) && mclk.pll) { + ram_wr32(fuc, 0x004000, (ctrl |= 0x00000008)); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00001000); + ram_wr32(fuc, 0x004000, (ctrl &= ~0x00000001)); + ram_wr32(fuc, 0x004004, mclk.pll); + ram_wr32(fuc, 0x004000, (ctrl |= 0x00000001)); + udelay(64); + ram_wr32(fuc, 0x004018, 0x00005000 | r004018); + udelay(20); + } else + if (!mclk.pll) { + ram_mask(fuc, 0x004168, 0x003f3040, mclk.clk); + ram_wr32(fuc, 0x004000, (ctrl |= 0x00000008)); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x0000d000 | r004018); + } + + if ( (nv_ro08(bios, rammap.data + 0x04) & 0x08)) { + u32 unk5a0 = (nv_ro16(bios, ramcfg.data + 0x05) << 8) | + nv_ro08(bios, ramcfg.data + 0x05); + u32 unk5a4 = (nv_ro16(bios, ramcfg.data + 0x07)); + u32 unk804 = (nv_ro08(bios, ramcfg.data + 0x09) & 0xf0) << 16 | + (nv_ro08(bios, ramcfg.data + 0x03) & 0x0f) << 16 | + (nv_ro08(bios, ramcfg.data + 0x09) & 0x0f) | + 0x80000000; + ram_wr32(fuc, 0x1005a0, unk5a0); + ram_wr32(fuc, 0x1005a4, unk5a4); + ram_wr32(fuc, 0x10f804, unk804); + ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); + } else { + 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 (mclk.pll) { + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000); + ram_wr32(fuc, 0x004000, (ctrl &= ~0x00000008)); + } + + /*XXX: LEAVE */ + ram_wr32(fuc, 0x1002dc, 0x00000000); + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x100210, 0x80000000); + ram_nsec(fuc, 1000); + ram_nsec(fuc, 1000); + + ram_mask(fuc, mr[2], 0x00000000, 0x00000000); + ram_nsec(fuc, 1000); + ram_nuke(fuc, mr[0]); + ram_mask(fuc, mr[0], 0x00000000, 0x00000000); + ram_nsec(fuc, 1000); + + ram_mask(fuc, 0x100220[3], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[1], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[6], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[7], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[2], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[4], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[5], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[0], 0x00000000, 0x00000000); + ram_mask(fuc, 0x100220[8], 0x00000000, 0x00000000); + + data = (nv_ro08(bios, ramcfg.data + 0x02) & 0x08) ? 0x00000000 : 0x00001000; + ram_mask(fuc, 0x100200, 0x00001000, data); + + unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000010; + unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; + unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; + if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x20)) + unk714 |= 0xf0000000; + if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x04)) + unk714 |= 0x00000010; + ram_wr32(fuc, 0x100714, unk714); + + if (nv_ro08(bios, ramcfg.data + 0x02) & 0x01) + unk71c |= 0x00000100; + ram_wr32(fuc, 0x10071c, unk71c); + + if (nv_ro08(bios, ramcfg.data + 0x02) & 0x02) + unk718 |= 0x00000100; + ram_wr32(fuc, 0x100718, unk718); + + if (nv_ro08(bios, ramcfg.data + 0x02) & 0x10) + ram_wr32(fuc, 0x111100, 0x48000000); /*XXX*/ + + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[0], 0x100, 0x000); + ram_nsec(fuc, 1000); + + ram_nsec(fuc, 2000); + ram_nsec(fuc, 12000); + + ram_wr32(fuc, 0x611200, 0x00003330); + if ( (nv_ro08(bios, rammap.data + 0x04) & 0x02)) + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) { + 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 +nva3_ram_prog(struct nouveau_fb *pfb) +{ + struct nouveau_device *device = nv_device(pfb); + struct nva3_ram *ram = (void *)pfb->ram; + struct nva3_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + return 0; +} + +static void +nva3_ram_tidy(struct nouveau_fb *pfb) +{ + struct nva3_ram *ram = (void *)pfb->ram; + struct nva3_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, false); +} + +static int +nva3_ram_init(struct nouveau_object *object) +{ + struct nouveau_fb *pfb = (void *)object->parent; + struct nva3_ram *ram = (void *)object; + int ret, i; + + ret = nouveau_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_DDR3: { + static const u32 pattern[16] = { + 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee, + 0x00000000, 0x11111111, 0x44444444, 0xdddddddd, + 0x33333333, 0x55555555, 0x77777777, 0x66666666, + 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb, + }; + + nv_wr32(pfb, 0x100538, 0x10001ff6); /*XXX*/ + 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, 0x10f8e0, (i << 8) | i); + nv_wr32(pfb, 0x10f900, pattern[i % 16]); + nv_wr32(pfb, 0x10f920, pattern[i % 16]); + } + } + break; + default: + break; + } + + return 0; +} + +static int nva3_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 datasize, struct nouveau_object **pobject) { struct nva3_ram *ram; - int ret; + 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_DDR3: + ram->base.calc = nva3_ram_calc; + ram->base.prog = nva3_ram_prog; + ram->base.tidy = nva3_ram_tidy; + break; + default: + nv_warn(ram, "reclocking of this ram type unsupported\n"); + return 0; + } + + 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_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_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_0x100714 = ramfuc_reg(0x100714); + ram->fuc.r_0x100718 = ramfuc_reg(0x100718); + ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); + ram->fuc.r_0x100760 = ramfuc_reg(0x100760); + ram->fuc.r_0x1007a0 = ramfuc_reg(0x1007a0); + ram->fuc.r_0x1007e0 = ramfuc_reg(0x1007e0); + ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); + ram->fuc.r_0x1110e0 = ramfuc_reg(0x1110e0); + ram->fuc.r_0x111100 = ramfuc_reg(0x111100); + ram->fuc.r_0x111104 = ramfuc_reg(0x111104); + 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); + } + return 0; } @@ -49,7 +441,7 @@ nva3_ram_oclass = { .ofuncs = &(struct nouveau_ofuncs) { .ctor = nva3_ram_ctor, .dtor = _nouveau_ram_dtor, - .init = _nouveau_ram_init, + .init = nva3_ram_init, .fini = _nouveau_ram_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c index f81afb8..f464547 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c @@ -23,10 +23,414 @@ */ #include <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/pll.h> +#include <subdev/bios/rammap.h> +#include <subdev/bios/timing.h> #include <subdev/ltcg.h> -#include "priv.h" -#include "nv50.h" +#include <subdev/clock.h> +#include <subdev/clock/pll.h> + +#include <core/option.h> + +#include "ramfuc.h" + +#include "nvc0.h" + +struct nvc0_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 nvc0_ram { + struct nouveau_ram base; + struct nvc0_ramfuc fuc; + struct nvbios_pll refpll; + struct nvbios_pll mempll; +}; + +static void +nvc0_ram_train(struct nvc0_ramfuc *fuc, u32 magic) +{ + struct nvc0_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nouveau_fb *pfb = nouveau_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 +nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq) +{ + struct nouveau_clock *clk = nouveau_clock(pfb); + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nvc0_ram *ram = (void *)pfb->ram; + struct nvc0_ramfuc *fuc = &ram->fuc; + struct bit_entry M; + u8 ver, cnt, strap; + u32 data; + 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_rammap_match(bios, freq / 1000, &ver, &rammap.size, + &cnt, &ramcfg.size); + 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 */ + if (bit_entry(bios, 'M', &M) || M.version != 2 || M.length < 3) { + nv_error(pfb, "invalid/missing memory table\n"); + return -EINVAL; + } + + strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2; + data = nv_ro16(bios, M.offset + 1); + if (data) + strap = nv_ro08(bios, data + strap); + + 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_timing_entry(bios, strap, &ver, + &timing.size); + 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 = nva3_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 = nva3_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) + nvc0_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); + nvc0_ram_train(fuc, 0x80021001); + nvc0_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); + + nvc0_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); + + nvc0_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 +nvc0_ram_prog(struct nouveau_fb *pfb) +{ + struct nouveau_device *device = nv_device(pfb); + struct nvc0_ram *ram = (void *)pfb->ram; + struct nvc0_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + return 0; +} + +static void +nvc0_ram_tidy(struct nouveau_fb *pfb) +{ + struct nvc0_ram *ram = (void *)pfb->ram; + struct nvc0_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, false); +} extern const u8 nvc0_pte_storage_type_map[256]; @@ -111,10 +515,9 @@ nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, return 0; } -static int -nvc0_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +int +nvc0_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, int size, void **pobject) { struct nouveau_fb *pfb = nouveau_fb(parent); struct nouveau_bios *bios = nouveau_bios(pfb); @@ -128,8 +531,8 @@ nvc0_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, bool uniform = true; int ret, part; - ret = nouveau_ram_create(parent, engine, oclass, &ram); - *pobject = nv_object(ram); + ret = nouveau_ram_create_(parent, engine, oclass, size, pobject); + ram = *pobject; if (ret) return ret; @@ -183,13 +586,158 @@ nvc0_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, return 0; } +static int +nvc0_ram_init(struct nouveau_object *object) +{ + struct nouveau_fb *pfb = (void *)object->parent; + struct nvc0_ram *ram = (void *)object; + int ret, i; + + ret = nouveau_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 +nvc0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_bios *bios = nouveau_bios(parent); + struct nvc0_ram *ram; + int ret; + + ret = nvc0_ram_create(parent, engine, oclass, &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 = nvc0_ram_calc; + ram->base.prog = nvc0_ram_prog; + ram->base.tidy = nvc0_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 nouveau_oclass nvc0_ram_oclass = { .handle = 0, .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nvc0_ram_create, + .ctor = nvc0_ram_ctor, .dtor = _nouveau_ram_dtor, - .init = _nouveau_ram_init, + .init = nvc0_ram_init, .fini = _nouveau_ram_fini, } }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c new file mode 100644 index 0000000..bc86cfd --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c @@ -0,0 +1,1264 @@ +/* + * 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 <subdev/gpio.h> + +#include <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/pll.h> +#include <subdev/bios/init.h> +#include <subdev/bios/rammap.h> +#include <subdev/bios/timing.h> + +#include <subdev/clock.h> +#include <subdev/clock/pll.h> + +#include <subdev/timer.h> + +#include <core/option.h> + +#include "nvc0.h" + +#include "ramfuc.h" + +struct nve0_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_0x10f750; +}; + +struct nve0_ram { + struct nouveau_ram base; + struct nve0_ramfuc fuc; + int from; + int mode; + int N1, fN1, M1, P1; + int N2, M2, P2; +}; + +/******************************************************************************* + * GDDR5 + ******************************************************************************/ +static void +train(struct nve0_ramfuc *fuc, u32 magic) +{ + struct nve0_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nouveau_fb *pfb = nouveau_fb(ram); + const int mc = nv_rd32(pfb, 0x02243c); + int i; + + ram_mask(fuc, 0x10f910, 0xbc0e0000, magic); + ram_mask(fuc, 0x10f914, 0xbc0e0000, magic); + for (i = 0; i < mc; i++) { + const u32 addr = 0x110974 + (i * 0x1000); + ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000); + } +} + +static void +r1373f4_init(struct nve0_ramfuc *fuc) +{ + struct nve0_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, 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 nve0_ramfuc *fuc, u32 ramcfg) +{ + struct nve0_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nouveau_bios *bios = nouveau_bios(ram); + u8 v0 = (nv_ro08(bios, ramcfg + 0x03) & 0xc0) >> 6; + u8 v1 = (nv_ro08(bios, ramcfg + 0x03) & 0x30) >> 4; + 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 int +nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) +{ + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nve0_ram *ram = (void *)pfb->ram; + struct nve0_ramfuc *fuc = &ram->fuc; + const u32 rammap = ram->base.rammap.data; + const u32 ramcfg = ram->base.ramcfg.data; + const u32 timing = ram->base.timing.data; + int vc = !(nv_ro08(bios, ramcfg + 0x02) & 0x08); + int mv = 1; /*XXX*/ + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + 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); + + 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); + + ram_mask(fuc, 0x10f914, 0x01020000, 0x000c0000); + ram_mask(fuc, 0x10f910, 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 (nv_ro08(bios, ramcfg + 0x03) & 0xc0) { + case 0xc0: data &= ~0x00000040; break; + case 0x80: data &= ~0x00000100; break; + case 0x40: data &= ~0x80000000; break; + case 0x00: data &= ~0x00000400; break; + } + + switch (nv_ro08(bios, ramcfg + 0x03) & 0x30) { + case 0x30: data &= ~0x00000020; break; + case 0x20: data &= ~0x00000080; break; + case 0x10: data &= ~0x00080000; break; + case 0x00: data &= ~0x00000200; break; + } + } + + if (nv_ro08(bios, ramcfg + 0x02) & 0x80) + mask |= 0x03000000; + if (nv_ro08(bios, ramcfg + 0x02) & 0x40) + mask |= 0x00002000; + if (nv_ro08(bios, ramcfg + 0x07) & 0x10) + mask |= 0x00004000; + if (nv_ro08(bios, ramcfg + 0x07) & 0x08) + 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, 0x00008000, 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, ramcfg); + ram_mask(fuc, 0x10f830, 0x00c00000, 0x00240001); + } else + if (ram->from != 2 && ram->mode != 2) { + r1373f4_init(fuc); + r1373f4_fini(fuc, ramcfg); + } + + 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 ( (nv_ro08(bios, ramcfg + 0x02) & 0x40) || + (nv_ro08(bios, ramcfg + 0x07) & 0x10)) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->from != 2 && ram->mode == 2) { + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000); + ram_mask(fuc, 0x1373f0, 0x00000000, 0x00000002); + ram_mask(fuc, 0x10f830, 0x00800001, 0x00408010); + r1373f4_init(fuc); + r1373f4_fini(fuc, ramcfg); + 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, ramcfg); + } + + if (ram->mode != 2) /*XXX*/ { + if (nv_ro08(bios, ramcfg + 0x07) & 0x40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + data = (nv_ro08(bios, rammap + 0x11) & 0x0c) >> 2; + ram_wr32(fuc, 0x10f65c, 0x00000011 * data); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * nv_ro08(bios, ramcfg + 0x09)); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * nv_ro08(bios, ramcfg + 0x09)); + + data = nv_ro08(bios, ramcfg + 0x04); + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) { + ram_wr32(fuc, 0x10f698, 0x01010101 * data); + ram_wr32(fuc, 0x10f69c, 0x01010101 * data); + } + + if (ram->mode != 2) { + u32 temp = ram_rd32(fuc, 0x10f694) & ~0xff00ff00; + ram_wr32(fuc, 0x10f694, temp | (0x01000100 * data)); + } + + if (ram->mode == 2 && (nv_ro08(bios, ramcfg + 0x08) & 0x10)) + data = 0x00000080; + else + data = 0x00000000; + ram_mask(fuc, 0x10f60c, 0x00000080, data); + + mask = 0x00070000; + data = 0x00000000; + if (!(nv_ro08(bios, ramcfg + 0x02) & 0x80)) + data |= 0x03000000; + if (!(nv_ro08(bios, ramcfg + 0x02) & 0x40)) + data |= 0x00002000; + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x10)) + data |= 0x00004000; + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) + data |= 0x00000003; + else + data |= 0x74000000; + ram_mask(fuc, 0x10f824, mask, data); + + if (nv_ro08(bios, ramcfg + 0x01) & 0x08) + 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 (nv_ro08(bios, ramcfg + 0x08) & 0x01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + data = 0x00000000; + if (nv_ro08(bios, ramcfg + 0x08) & 0x08) + data |= 0x00002000; + if (nv_ro08(bios, ramcfg + 0x08) & 0x04) + data |= 0x00001000; + if (nv_ro08(bios, ramcfg + 0x08) & 0x02) + data |= 0x00004000; + ram_mask(fuc, 0x10f830, 0x00007000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, nv_ro32(bios, timing + 0x28)); + ram_mask(fuc, 0x10f290, 0xffffffff, nv_ro32(bios, timing + 0x00)); + ram_mask(fuc, 0x10f294, 0xffffffff, nv_ro32(bios, timing + 0x04)); + ram_mask(fuc, 0x10f298, 0xffffffff, nv_ro32(bios, timing + 0x08)); + ram_mask(fuc, 0x10f29c, 0xffffffff, nv_ro32(bios, timing + 0x0c)); + ram_mask(fuc, 0x10f2a0, 0xffffffff, nv_ro32(bios, timing + 0x10)); + ram_mask(fuc, 0x10f2a4, 0xffffffff, nv_ro32(bios, timing + 0x14)); + ram_mask(fuc, 0x10f2a8, 0xffffffff, nv_ro32(bios, timing + 0x18)); + ram_mask(fuc, 0x10f2ac, 0xffffffff, nv_ro32(bios, timing + 0x1c)); + ram_mask(fuc, 0x10f2cc, 0xffffffff, nv_ro32(bios, timing + 0x20)); + ram_mask(fuc, 0x10f2e8, 0xffffffff, nv_ro32(bios, timing + 0x24)); + + data = (nv_ro08(bios, ramcfg + 0x02) & 0x03) << 8; + if (nv_ro08(bios, ramcfg + 0x01) & 0x10) + data |= 0x70000000; + ram_mask(fuc, 0x10f604, 0x70000300, data); + + data = (nv_ro08(bios, timing + 0x30) & 0x07) << 28; + if (nv_ro08(bios, ramcfg + 0x01) & 0x01) + data |= 0x00000100; + ram_mask(fuc, 0x10f614, 0x70000000, data); + + data = (nv_ro08(bios, timing + 0x30) & 0x07) << 28; + if (nv_ro08(bios, ramcfg + 0x01) & 0x02) + data |= 0x00000100; + ram_mask(fuc, 0x10f610, 0x70000000, data); + + mask = 0x33f00000; + data = 0x00000000; + if (!(nv_ro08(bios, ramcfg + 0x01) & 0x04)) + data |= 0x20200000; + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80)) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if ( (nv_ro08(bios, ramcfg + 0x03) & 0xf0)) { + if (nv_ro08(bios, rammap + 0x08) & 0x0c) { + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80)) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x00000004; + } + } else { + mask |= 0x40000020; + data |= 0x00000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + data = nv_ro08(bios, ramcfg + 0x03) & 0x0f; + ram_wr32(fuc, 0x10f870, 0x11111111 * data); + + data = nv_ro08(bios, ramcfg + 0x02) & 0x03; + if (nv_ro08(bios, ramcfg + 0x01) & 0x10) + data |= 0x00000004; + if ((nv_rd32(bios, 0x100770) & 0x00000004) != (data & 0x00000004)) { + ram_wr32(fuc, 0x10f750, 0x04000009); + ram_wr32(fuc, 0x100710, 0x00000000); + ram_wait(fuc, 0x100710, 0x80000000, 0x80000000, 200000); + } + ram_mask(fuc, 0x100770, 0x00000007, data); + + data = (nv_ro08(bios, timing + 0x30) & 0x07) << 8; + if (nv_ro08(bios, ramcfg + 0x01) & 0x01) + data |= 0x80000000; + ram_mask(fuc, 0x100778, 0x00000700, data); + + data = nv_ro16(bios, timing + 0x2c); + ram_mask(fuc, 0x10f250, 0x000003f0, (data & 0x003f) << 4); + ram_mask(fuc, 0x10f24c, 0x7f000000, (data & 0x1fc0) << 18); + + data = nv_ro08(bios, timing + 0x30); + ram_mask(fuc, 0x10f224, 0x001f0000, (data & 0xf8) << 13); + + data = nv_ro16(bios, timing + 0x31); + ram_mask(fuc, 0x10fec4, 0x041e0f07, (data & 0x0800) << 15 | + (data & 0x0780) << 10 | + (data & 0x0078) << 5 | + (data & 0x0007)); + ram_mask(fuc, 0x10fec8, 0x00000027, (data & 0x8000) >> 10 | + (data & 0x7000) >> 12); + + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ + + if ((nv_ro08(bios, ramcfg + 0x08) & 0x10) && (ram->mode == 2) /*XXX*/) { + u32 temp = ram_mask(fuc, 0x10f294, 0xff000000, 0x24000000); + train(fuc, 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]); + 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); + + data = ram_rd32(fuc, 0x10f978); + data &= ~0x00046144; + data |= 0x0000000b; + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) { + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x04)) + 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 (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) { + data = 0x88020000; + if ( (nv_ro08(bios, ramcfg + 0x07) & 0x04)) + data |= 0x10000000; + if (!(nv_ro08(bios, rammap + 0x08) & 0x10)) + data |= 0x00080000; + } else { + data = 0xa40e0000; + } + train(fuc, data); + ram_nsec(fuc, 1000); + + if (ram->mode == 2) { /*XXX*/ + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000004); + } + + /* MR5: (re)enable LP3 if necessary + * XXX: need to find the switch, keeping off for now + */ + ram_mask(fuc, mr[5], 0x00000004, 0x00000000); + + if (ram->mode != 2) { + ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + } + + if (nv_ro08(bios, ramcfg + 0x07) & 0x02) { + ram_mask(fuc, 0x10f910, 0x80020000, 0x01000000); + ram_mask(fuc, 0x10f914, 0x80020000, 0x01000000); + } + + ram_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (nv_ro08(bios, rammap + 0x08) & 0x01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + return 0; +} + +/******************************************************************************* + * DDR3 + ******************************************************************************/ + +static int +nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) +{ + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nve0_ram *ram = (void *)pfb->ram; + struct nve0_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; + const u32 rammap = ram->base.rammap.data; + const u32 ramcfg = ram->base.ramcfg.data; + const u32 timing = ram->base.timing.data; + int vc = !(nv_ro08(bios, ramcfg + 0x02) & 0x08); + int mv = 1; /*XXX*/ + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + 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 ((nv_ro08(bios, ramcfg + 0x03) & 0xf0)) + 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 (nv_ro08(bios, ramcfg + 0x03) & 0xc0) { + case 0xc0: data &= ~0x00000040; break; + case 0x80: data &= ~0x00000100; break; + case 0x40: data &= ~0x80000000; break; + case 0x00: data &= ~0x00000400; break; + } + + switch (nv_ro08(bios, ramcfg + 0x03) & 0x30) { + case 0x30: data &= ~0x00000020; break; + case 0x20: data &= ~0x00000080; break; + case 0x10: data &= ~0x00080000; break; + case 0x00: data &= ~0x00000200; break; + } + } + + if (nv_ro08(bios, ramcfg + 0x02) & 0x80) + mask |= 0x03000000; + if (nv_ro08(bios, ramcfg + 0x02) & 0x40) + mask |= 0x00002000; + if (nv_ro08(bios, ramcfg + 0x07) & 0x10) + mask |= 0x00004000; + if (nv_ro08(bios, ramcfg + 0x07) & 0x08) + 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 |= (nv_ro08(bios, ramcfg + 0x03) & 0x30) << 12; + 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 ( (nv_ro08(bios, ramcfg + 0x02) & 0x40) || + (nv_ro08(bios, ramcfg + 0x07) & 0x10)) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->mode != 2) /*XXX*/ { + if (nv_ro08(bios, ramcfg + 0x07) & 0x40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + data = (nv_ro08(bios, rammap + 0x11) & 0x0c) >> 2; + ram_wr32(fuc, 0x10f65c, 0x00000011 * data); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * nv_ro08(bios, ramcfg + 0x09)); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * nv_ro08(bios, ramcfg + 0x09)); + + mask = 0x00010000; + data = 0x00000000; + if (!(nv_ro08(bios, ramcfg + 0x02) & 0x80)) + data |= 0x03000000; + if (!(nv_ro08(bios, ramcfg + 0x02) & 0x40)) + data |= 0x00002000; + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x10)) + data |= 0x00004000; + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x08)) + data |= 0x00000003; + else + data |= 0x14000000; + ram_mask(fuc, 0x10f824, mask, data); + ram_nsec(fuc, 1000); + + if (nv_ro08(bios, ramcfg + 0x08) & 0x01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, nv_ro32(bios, timing + 0x28)); + ram_mask(fuc, 0x10f290, 0xffffffff, nv_ro32(bios, timing + 0x00)); + ram_mask(fuc, 0x10f294, 0xffffffff, nv_ro32(bios, timing + 0x04)); + ram_mask(fuc, 0x10f298, 0xffffffff, nv_ro32(bios, timing + 0x08)); + ram_mask(fuc, 0x10f29c, 0xffffffff, nv_ro32(bios, timing + 0x0c)); + ram_mask(fuc, 0x10f2a0, 0xffffffff, nv_ro32(bios, timing + 0x10)); + ram_mask(fuc, 0x10f2a4, 0xffffffff, nv_ro32(bios, timing + 0x14)); + ram_mask(fuc, 0x10f2a8, 0xffffffff, nv_ro32(bios, timing + 0x18)); + ram_mask(fuc, 0x10f2ac, 0xffffffff, nv_ro32(bios, timing + 0x1c)); + ram_mask(fuc, 0x10f2cc, 0xffffffff, nv_ro32(bios, timing + 0x20)); + ram_mask(fuc, 0x10f2e8, 0xffffffff, nv_ro32(bios, timing + 0x24)); + + mask = 0x33f00000; + data = 0x00000000; + if (!(nv_ro08(bios, ramcfg + 0x01) & 0x04)) + data |= 0x20200000; + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80)) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if ( (nv_ro08(bios, ramcfg + 0x03) & 0xf0)) { + if (nv_ro08(bios, rammap + 0x08) & 0x0c) { + if (!(nv_ro08(bios, ramcfg + 0x07) & 0x80)) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x08000004; + } + data |= 0x04000000; + } else { + mask |= 0x44000020; + data |= 0x08000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + data = nv_ro08(bios, ramcfg + 0x03) & 0x0f; + ram_wr32(fuc, 0x10f870, 0x11111111 * data); + + data = nv_ro16(bios, timing + 0x2c); + ram_mask(fuc, 0x10f250, 0x000003f0, (data & 0x003f) << 4); + + if (((nv_ro32(bios, timing + 0x2c) & 0x00001fc0) >> 6) > + ((nv_ro32(bios, timing + 0x28) & 0x7f000000) >> 24)) + data = (nv_ro32(bios, timing + 0x2c) & 0x00001fc0) >> 6; + else + data = (nv_ro32(bios, timing + 0x28) & 0x1f000000) >> 24; + ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); + + data = nv_ro08(bios, timing + 0x30); + ram_mask(fuc, 0x10f224, 0x001f0000, (data & 0xf8) << 13); + + 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_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (nv_ro08(bios, rammap + 0x08) & 0x01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + return 0; +} + +/******************************************************************************* + * main hooks + ******************************************************************************/ + +static int +nve0_ram_calc(struct nouveau_fb *pfb, u32 freq) +{ + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nve0_ram *ram = (void *)pfb->ram; + struct nve0_ramfuc *fuc = &ram->fuc; + struct bit_entry M; + int ret, refclk, strap, i; + u32 data; + u8 cnt; + + /* lookup memory config data relevant to the target frequency */ + ram->base.rammap.data = nvbios_rammap_match(bios, freq / 1000, + &ram->base.rammap.version, + &ram->base.rammap.size, &cnt, + &ram->base.ramcfg.size); + if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 || + ram->base.rammap.size < 0x09) { + nv_error(pfb, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + if (bit_entry(bios, 'M', &M) || M.version != 2 || M.length < 3) { + nv_error(pfb, "invalid/missing memory table\n"); + return -EINVAL; + } + + strap = (nv_rd32(pfb, 0x101000) & 0x0000003c) >> 2; + data = nv_ro16(bios, M.offset + 1); + if (data) + strap = nv_ro08(bios, data + strap); + + if (strap >= cnt) { + nv_error(pfb, "invalid ramcfg strap\n"); + return -EINVAL; + } + + ram->base.ramcfg.version = ram->base.rammap.version; + ram->base.ramcfg.data = ram->base.rammap.data + ram->base.rammap.size + + (ram->base.ramcfg.size * strap); + if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 || + ram->base.ramcfg.size < 0x08) { + nv_error(pfb, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + strap = nv_ro08(bios, ram->base.ramcfg.data + 0x00); + if (strap != 0xff) { + ram->base.timing.data = + nvbios_timing_entry(bios, strap, + &ram->base.timing.version, + &ram->base.timing.size); + if (!ram->base.timing.data || + ram->base.timing.version != 0x20 || + ram->base.timing.size < 0x33) { + nv_error(pfb, "invalid/missing timing entry\n"); + return -EINVAL; + } + } else { + ram->base.timing.data = 0; + } + + ret = ram_init(fuc, pfb); + if (ret) + return ret; + + ram->mode = (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 = freq; + if (ram->mode == 2) + refclk = fuc->mempll.refclk; + + /* calculate refpll coefficients */ + ret = nva3_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 = nva3_pll_calc(nv_subdev(pfb), &fuc->mempll, 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]); + } + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + ret = nouveau_sddr3_calc(&ram->base); + if (ret == 0) + ret = nve0_ram_calc_sddr3(pfb, freq); + break; + case NV_MEM_TYPE_GDDR5: + ret = nouveau_gddr5_calc(&ram->base); + if (ret == 0) + ret = nve0_ram_calc_gddr5(pfb, freq); + break; + default: + ret = -ENOSYS; + break; + } + + return ret; +} + +static int +nve0_ram_prog(struct nouveau_fb *pfb) +{ + struct nouveau_device *device = nv_device(pfb); + struct nve0_ram *ram = (void *)pfb->ram; + struct nve0_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + return 0; +} + +static void +nve0_ram_tidy(struct nouveau_fb *pfb) +{ + struct nve0_ram *ram = (void *)pfb->ram; + struct nve0_ramfuc *fuc = &ram->fuc; + ram_exec(fuc, false); +} + +static int +nve0_ram_init(struct nouveau_object *object) +{ + struct nouveau_fb *pfb = (void *)object->parent; + struct nve0_ram *ram = (void *)object; + struct nouveau_bios *bios = nouveau_bios(pfb); + static const u8 train0[] = { + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + }; + static const u32 train1[] = { + 0x00000000, 0xffffffff, + 0x55555555, 0xaaaaaaaa, + 0x33333333, 0xcccccccc, + 0xf0f0f0f0, 0x0f0f0f0f, + 0x00ff00ff, 0xff00ff00, + 0x0000ffff, 0xffff0000, + }; + u8 ver, hdr, cnt, len, snr, ssz; + u32 data, save; + int ret, i; + + ret = nouveau_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_rammap_table(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); + for (i = 0; i < cnt; i++) { + nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4); + nvbios_exec(&(struct nvbios_init) { + .subdev = nv_subdev(pfb), + .bios = bios, + .offset = nv_ro32(bios, data), /* guess u32 */ + .execute = 1, + }); + data += 4; + } + nv_wr32(pfb, 0x10f65c, save); + + switch (ram->base.type) { + case NV_MEM_TYPE_GDDR5: + for (i = 0; i < 0x30; i++) { + nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]); + nv_wr32(pfb, 0x10f918, train1[i % 12]); + nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]); + nv_wr32(pfb, 0x10f918, train1[i % 12]); + + nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]); + nv_wr32(pfb, 0x10f91c, train1[i % 12]); + nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]); + nv_wr32(pfb, 0x10f91c, train1[i % 12]); + } + + for (i = 0; i < 0x100; i++) { + nv_wr32(pfb, 0x10f968, i); + nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]); + } + + for (i = 0; i < 0x100; i++) { + nv_wr32(pfb, 0x10f96c, i); + nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]); + } + break; + default: + break; + } + + return 0; +} + +static int +nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nouveau_gpio *gpio = nouveau_gpio(pfb); + struct dcb_gpio_func func; + struct nve0_ram *ram; + int ret; + + ret = nvc0_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (ram->base.type) { + case NV_MEM_TYPE_DDR3: + case NV_MEM_TYPE_GDDR5: + ram->base.calc = nve0_ram_calc; + ram->base.prog = nve0_ram_prog; + ram->base.tidy = nve0_ram_tidy; + break; + default: + nv_warn(pfb, "reclocking of this RAM type is unsupported\n"); + break; + } + + // 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; + } + + 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_0x10f750 = ramfuc_reg(0x10f750); + return 0; +} + +struct nouveau_oclass +nve0_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_ram_ctor, + .dtor = _nouveau_ram_dtor, + .init = nve0_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramseq.h b/drivers/gpu/drm/nouveau/core/subdev/fb/ramseq.h new file mode 100644 index 0000000..571077e --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramseq.h @@ -0,0 +1,18 @@ +#ifndef __NVKM_FBRAM_SEQ_H__ +#define __NVKM_FBRAM_SEQ_H__ + +#include <subdev/bus.h> +#include <subdev/bus/hwsq.h> + +#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/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c new file mode 100644 index 0000000..ebd4cd9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c @@ -0,0 +1,99 @@ +/* + * 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 <bskeggs@redhat.com> + */ + +#include <subdev/bios.h> +#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 +nouveau_sddr3_calc(struct nouveau_ram *ram) +{ + struct nouveau_bios *bios = nouveau_bios(ram); + int WL, CL, WR; + + switch (!!ram->timing.data * ram->timing.version) { + case 0x20: + WL = (nv_ro16(bios, ram->timing.data + 0x04) & 0x0f80) >> 7; + CL = nv_ro08(bios, ram->timing.data + 0x04) & 0x1f; + WR = nv_ro08(bios, ram->timing.data + 0x0a) & 0x7f; + break; + default: + return -ENOSYS; + } + + WL = ramxlat(ramddr3_cwl, WL); + CL = ramxlat(ramddr3_cl, CL); + WR = ramxlat(ramddr3_wr, WR); + if (WL < 0 || CL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xe74; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x0e) << 3; + ram->mr[0] |= (CL & 0x01) << 2; + + ram->mr[2] &= ~0x038; + ram->mr[2] |= (WL & 0x07) << 3; + return 0; +} |