From 2c3c1048746a4622d8c89a29670120dc8fab93c4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:49:45 +0200 Subject: Adding upstream version 6.1.76. Signed-off-by: Daniel Baumann --- drivers/gpu/drm/nouveau/nvkm/Kbuild | 6 + drivers/gpu/drm/nouveau/nvkm/core/Kbuild | 16 + drivers/gpu/drm/nouveau/nvkm/core/client.c | 307 ++ drivers/gpu/drm/nouveau/nvkm/core/engine.c | 204 ++ drivers/gpu/drm/nouveau/nvkm/core/enum.c | 56 + drivers/gpu/drm/nouveau/nvkm/core/event.c | 100 + drivers/gpu/drm/nouveau/nvkm/core/firmware.c | 109 + drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c | 278 ++ drivers/gpu/drm/nouveau/nvkm/core/ioctl.c | 459 +++ drivers/gpu/drm/nouveau/nvkm/core/memory.c | 154 + drivers/gpu/drm/nouveau/nvkm/core/mm.c | 307 ++ drivers/gpu/drm/nouveau/nvkm/core/notify.c | 163 + drivers/gpu/drm/nouveau/nvkm/core/object.c | 336 ++ drivers/gpu/drm/nouveau/nvkm/core/oproxy.c | 209 ++ drivers/gpu/drm/nouveau/nvkm/core/option.c | 139 + drivers/gpu/drm/nouveau/nvkm/core/ramht.c | 162 + drivers/gpu/drm/nouveau/nvkm/core/subdev.c | 194 ++ drivers/gpu/drm/nouveau/nvkm/engine/Kbuild | 25 + drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild | 2 + drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c | 44 + drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild | 10 + drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc | 864 +++++ .../gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 | 2 + .../drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h | 607 ++++ .../gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 | 2 + .../drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h | 621 ++++ drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c | 69 + drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c | 101 + drivers/gpu/drm/nouveau/nvkm/engine/ce/gm107.c | 43 + drivers/gpu/drm/nouveau/nvkm/engine/ce/gm200.c | 42 + drivers/gpu/drm/nouveau/nvkm/engine/ce/gp100.c | 102 + drivers/gpu/drm/nouveau/nvkm/engine/ce/gp102.c | 44 + drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c | 83 + drivers/gpu/drm/nouveau/nvkm/engine/ce/gv100.c | 64 + drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h | 11 + drivers/gpu/drm/nouveau/nvkm/engine/ce/tu102.c | 41 + drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild | 2 + drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c | 134 + drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild | 7 + drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c | 58 + drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h | 9 + drivers/gpu/drm/nouveau/nvkm/engine/device/base.c | 3196 +++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c | 212 ++ drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.h | 13 + drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c | 1695 ++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h | 60 + drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c | 375 +++ drivers/gpu/drm/nouveau/nvkm/engine/device/user.c | 428 +++ drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 36 + drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c | 477 +++ drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.c | 275 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.h | 135 + drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c | 134 + drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h | 36 + drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c | 864 +++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h | 101 + drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c | 327 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c | 377 +++ drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c | 153 + drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c | 1240 ++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c | 311 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c | 59 + drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c | 114 + drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c | 182 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c | 87 + drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c | 200 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c | 109 + drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c | 208 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c | 1235 ++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c | 84 + drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h | 16 + drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c | 105 + drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h | 62 + drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c | 72 + drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h | 191 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c | 74 + drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c | 88 + drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c | 130 + drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c | 1643 ++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c | 338 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h | 87 + drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h | 89 + .../gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c | 62 + .../gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c | 250 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c | 239 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c | 117 + drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c | 115 + drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c | 129 + drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c | 205 ++ drivers/gpu/drm/nouveau/nvkm/engine/dma/Kbuild | 14 + drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c | 116 + drivers/gpu/drm/nouveau/nvkm/engine/dma/gf100.c | 37 + drivers/gpu/drm/nouveau/nvkm/engine/dma/gf119.c | 37 + drivers/gpu/drm/nouveau/nvkm/engine/dma/gv100.c | 35 + drivers/gpu/drm/nouveau/nvkm/engine/dma/nv04.c | 37 + drivers/gpu/drm/nouveau/nvkm/engine/dma/nv50.c | 37 + drivers/gpu/drm/nouveau/nvkm/engine/dma/priv.h | 19 + drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c | 138 + drivers/gpu/drm/nouveau/nvkm/engine/dma/user.h | 21 + .../gpu/drm/nouveau/nvkm/engine/dma/usergf100.c | 150 + .../gpu/drm/nouveau/nvkm/engine/dma/usergf119.c | 132 + .../gpu/drm/nouveau/nvkm/engine/dma/usergv100.c | 119 + drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv04.c | 134 + drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv50.c | 157 + drivers/gpu/drm/nouveau/nvkm/engine/falcon.c | 355 +++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild | 40 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c | 357 +++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.h | 11 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c | 393 +++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h | 35 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c | 263 ++ .../gpu/drm/nouveau/nvkm/engine/fifo/changf100.h | 29 + .../gpu/drm/nouveau/nvkm/engine/fifo/changk104.h | 52 + .../gpu/drm/nouveau/nvkm/engine/fifo/channv04.h | 29 + .../gpu/drm/nouveau/nvkm/engine/fifo/channv50.c | 276 ++ .../gpu/drm/nouveau/nvkm/engine/fifo/channv50.h | 53 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c | 226 ++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv10.c | 97 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv17.c | 98 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv40.c | 254 ++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c | 132 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga102.c | 311 ++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c | 699 ++++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.h | 38 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c | 1249 ++++++++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h | 168 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c | 67 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c | 64 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c | 45 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c | 113 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c | 61 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c | 45 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c | 98 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c | 46 + .../gpu/drm/nouveau/nvkm/engine/fifo/gpfifog84.c | 95 + .../gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c | 308 ++ .../gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c | 361 +++ .../gpu/drm/nouveau/nvkm/engine/fifo/gpfifogv100.c | 241 ++ .../gpu/drm/nouveau/nvkm/engine/fifo/gpfifonv50.c | 93 + .../gpu/drm/nouveau/nvkm/engine/fifo/gpfifotu102.c | 81 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c | 308 ++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c | 399 +++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h | 23 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c | 61 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c | 99 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c | 130 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c | 149 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h | 20 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h | 48 + .../gpu/drm/nouveau/nvkm/engine/fifo/regsnv04.h | 133 + drivers/gpu/drm/nouveau/nvkm/engine/fifo/tu102.c | 477 +++ drivers/gpu/drm/nouveau/nvkm/engine/fifo/user.h | 8 + .../gpu/drm/nouveau/nvkm/engine/fifo/usergv100.c | 45 + .../gpu/drm/nouveau/nvkm/engine/fifo/usertu102.c | 45 + drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild | 65 + drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c | 182 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c | 1603 ++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h | 275 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c | 107 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c | 810 +++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c | 355 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c | 310 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c | 525 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c | 1010 ++++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c | 865 +++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c | 105 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c | 570 ++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c | 86 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c | 995 ++++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm200.c | 128 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm20b.c | 87 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c | 139 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c | 119 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp104.c | 48 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp107.c | 56 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgv100.c | 214 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c | 693 ++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h | 131 + drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c | 3347 ++++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxtu102.c | 95 + drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc | 335 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc | 492 +++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h | 532 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h | 539 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h | 539 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h | 539 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h | 475 +++ .../drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 | 42 + .../drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h | 607 ++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc | 699 ++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h | 1049 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h | 1049 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h | 1046 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h | 1046 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h | 918 ++++++ .../drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 | 40 + .../drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h | 918 ++++++ .../gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc | 261 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h | 9 + drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c | 198 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c | 2489 +++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h | 421 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c | 158 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c | 157 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c | 133 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c | 198 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c | 224 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c | 503 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.h | 55 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c | 399 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c | 151 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c | 208 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c | 363 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c | 443 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gm200.c | 293 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gm20b.c | 187 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c | 162 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c | 158 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gp104.c | 99 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gp107.c | 88 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gp108.c | 98 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gp10b.c | 100 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c | 49 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/gr/gv100.c | 147 + drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c | 48 + drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c | 1426 +++++++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c | 1221 +++++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h | 14 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv15.c | 59 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv17.c | 59 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c | 376 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h | 35 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c | 135 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c | 126 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c | 200 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c | 137 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c | 137 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c | 476 +++ drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h | 49 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv44.c | 108 + drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c | 796 +++++ drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h | 37 + drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h | 45 + drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h | 275 ++ drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c | 204 ++ drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild | 6 + drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c | 44 + drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c | 299 ++ drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h | 32 + drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c | 83 + drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c | 217 ++ drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c | 136 + drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h | 17 + drivers/gpu/drm/nouveau/nvkm/engine/msenc/Kbuild | 2 + drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild | 6 + drivers/gpu/drm/nouveau/nvkm/engine/mspdec/base.c | 31 + drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c | 42 + drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gt215.c | 42 + drivers/gpu/drm/nouveau/nvkm/engine/mspdec/priv.h | 12 + drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild | 5 + drivers/gpu/drm/nouveau/nvkm/engine/msppp/base.c | 31 + drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/msppp/gt215.c | 42 + drivers/gpu/drm/nouveau/nvkm/engine/msppp/priv.h | 10 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild | 7 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/base.c | 31 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c | 50 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c | 42 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/gt215.c | 42 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/mcp89.c | 42 + drivers/gpu/drm/nouveau/nvkm/engine/msvld/priv.h | 12 + drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild | 3 + drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c | 61 + drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gm107.c | 63 + drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h | 19 + drivers/gpu/drm/nouveau/nvkm/engine/nvenc/Kbuild | 3 + drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c | 62 + drivers/gpu/drm/nouveau/nvkm/engine/nvenc/gm107.c | 63 + drivers/gpu/drm/nouveau/nvkm/engine/nvenc/priv.h | 19 + drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild | 11 + drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c | 868 +++++ drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c | 165 + drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c | 243 ++ drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h | 20 + drivers/gpu/drm/nouveau/nvkm/engine/pm/gf108.c | 66 + drivers/gpu/drm/nouveau/nvkm/engine/pm/gf117.c | 80 + drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c | 184 ++ drivers/gpu/drm/nouveau/nvkm/engine/pm/gt200.c | 157 + drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c | 138 + drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c | 123 + drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h | 15 + drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c | 175 + drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h | 105 + drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild | 2 + .../gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s | 698 ++++ .../drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h | 585 ++++ drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c | 81 + drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild | 5 + drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c | 118 + drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c | 350 ++ drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp108.c | 43 + drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h | 30 + drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c | 82 + drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild | 10 + drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c | 110 + drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c | 111 + drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h | 29 + drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c | 155 + drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c | 139 + drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c | 68 + drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c | 148 + drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h | 21 + drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.c | 85 + drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.h | 22 + drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h | 22 + drivers/gpu/drm/nouveau/nvkm/engine/vic/Kbuild | 2 + drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild | 2 + drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c | 43 + drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c | 191 ++ drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild | 6 + drivers/gpu/drm/nouveau/nvkm/falcon/base.c | 223 ++ drivers/gpu/drm/nouveau/nvkm/falcon/cmdq.c | 214 ++ drivers/gpu/drm/nouveau/nvkm/falcon/msgq.c | 213 ++ drivers/gpu/drm/nouveau/nvkm/falcon/priv.h | 5 + drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.c | 87 + drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.h | 89 + drivers/gpu/drm/nouveau/nvkm/falcon/v1.c | 311 ++ drivers/gpu/drm/nouveau/nvkm/nvfw/Kbuild | 7 + drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c | 164 + drivers/gpu/drm/nouveau/nvkm/nvfw/flcn.c | 115 + drivers/gpu/drm/nouveau/nvkm/nvfw/fw.c | 51 + drivers/gpu/drm/nouveau/nvkm/nvfw/hs.c | 62 + drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c | 108 + drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild | 26 + drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild | 10 + drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c | 440 +++ drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c | 487 +++ drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c | 136 + drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c | 285 ++ drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c | 113 + drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c | 59 + drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c | 177 ++ drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c | 253 ++ drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h | 154 + drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c | 231 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild | 9 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c | 142 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/g84.c | 63 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c | 196 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.h | 27 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm107.c | 66 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm20b.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c | 255 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.h | 29 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h | 34 + drivers/gpu/drm/nouveau/nvkm/subdev/bar/tu102.c | 99 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild | 41 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c | 129 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c | 135 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c | 135 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c | 107 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c | 205 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c | 49 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c | 126 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c | 97 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c | 122 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c | 235 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c | 174 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c | 232 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c | 110 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c | 94 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c | 150 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c | 164 + .../gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c | 129 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c | 83 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c | 2347 ++++++++++++++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c | 137 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c | 59 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c | 69 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c | 216 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c | 440 +++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c | 102 + .../drm/nouveau/nvkm/subdev/bios/power_budget.c | 126 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h | 29 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c | 78 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c | 258 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c | 242 ++ .../gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c | 140 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c | 84 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c | 134 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c | 123 + .../gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c | 64 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c | 212 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c | 173 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c | 121 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c | 160 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c | 88 + drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c | 74 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild | 8 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/base.c | 64 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c | 65 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c | 76 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c | 177 ++ drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h | 148 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c | 75 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c | 89 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c | 106 + drivers/gpu/drm/nouveau/nvkm/subdev/bus/priv.h | 19 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild | 15 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 716 +++++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c | 48 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c | 481 +++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c | 517 +++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c | 657 ++++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h | 159 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c | 1071 +++++++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c | 550 ++++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h | 19 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c | 422 +++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c | 84 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c | 233 ++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c | 563 ++++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h | 29 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h | 12 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c | 87 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c | 245 ++ drivers/gpu/drm/nouveau/nvkm/subdev/clk/priv.h | 27 + drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h | 15 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild | 18 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c | 135 + .../gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h | 83 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c | 68 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c | 66 + .../gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c | 77 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c | 120 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c | 60 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c | 186 ++ .../gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c | 152 + .../gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c | 79 + .../gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c | 67 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c | 465 +++ drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h | 23 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c | 143 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c | 113 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c | 79 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c | 178 ++ drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h | 30 + drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h | 24 + .../gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c | 112 + drivers/gpu/drm/nouveau/nvkm/subdev/fault/Kbuild | 7 + drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c | 183 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp100.c | 89 + drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp10b.c | 53 + drivers/gpu/drm/nouveau/nvkm/subdev/fault/gv100.c | 235 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fault/priv.h | 57 + drivers/gpu/drm/nouveau/nvkm/subdev/fault/tu102.c | 188 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c | 106 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild | 61 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c | 247 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c | 38 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c | 40 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c | 40 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c | 119 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c | 121 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c | 147 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h | 22 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c | 89 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.h | 35 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk110.c | 71 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c | 40 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c | 73 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm20b.c | 40 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp100.c | 77 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c | 138 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp10b.c | 37 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c | 38 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c | 55 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c | 37 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c | 37 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c | 50 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c | 70 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c | 102 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c | 60 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c | 133 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c | 62 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c | 62 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c | 68 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c | 62 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c | 71 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c | 57 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c | 45 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c | 45 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c | 43 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c | 288 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h | 22 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h | 87 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.c | 219 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h | 74 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h | 178 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c | 40 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c | 672 ++++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf108.c | 60 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c | 1716 ++++++++++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c | 51 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm200.c | 66 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c | 99 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c | 1007 ++++++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c | 86 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c | 65 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c | 40 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c | 56 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c | 48 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c | 223 ++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.h | 15 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c | 48 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c | 48 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c | 33 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c | 641 ++++ drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h | 17 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h | 23 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c | 100 + drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c | 120 + drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild | 5 + drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c | 54 + drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c | 54 + drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c | 43 + drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c | 52 + drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h | 13 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild | 8 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c | 256 ++ drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c | 75 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c | 119 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf119.c | 87 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c | 75 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c | 119 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c | 133 + drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h | 44 + drivers/gpu/drm/nouveau/nvkm/subdev/gsp/Kbuild | 3 + drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c | 57 + drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gv100.c | 56 + drivers/gpu/drm/nouveau/nvkm/subdev/gsp/priv.h | 15 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild | 33 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c | 278 ++ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c | 215 ++ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h | 46 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c | 194 ++ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c | 35 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c | 188 ++ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c | 431 +++ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c | 216 ++ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c | 264 ++ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h | 39 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c | 95 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c | 96 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c | 86 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c | 113 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c | 73 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c | 37 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c | 41 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c | 73 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c | 46 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm200.c | 48 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c | 37 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c | 37 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c | 37 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c | 116 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h | 68 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c | 76 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c | 51 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm200.c | 76 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c | 36 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c | 36 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c | 36 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h | 37 + .../gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild | 3 + .../gpu/drm/nouveau/nvkm/subdev/iccsense/base.c | 331 ++ .../gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c | 31 + .../gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h | 27 + drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild | 6 + drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c | 246 ++ .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c | 604 ++++ drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c | 230 ++ drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c | 263 ++ drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c | 400 +++ drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h | 33 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild | 9 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c | 143 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c | 256 ++ drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c | 57 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c | 152 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm200.c | 64 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp100.c | 76 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp102.c | 52 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c | 66 + drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h | 51 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild | 17 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c | 227 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mc/g84.c | 68 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c | 68 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c | 74 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c | 118 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk104.c | 66 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c | 41 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp100.c | 128 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp10b.c | 49 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/gt215.c | 77 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c | 86 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv11.c | 50 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv17.c | 59 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c | 54 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c | 61 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h | 63 + drivers/gpu/drm/nouveau/nvkm/subdev/mc/tu102.c | 136 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c | 437 +++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/g84.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c | 91 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk104.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk20a.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm200.c | 99 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm20b.c | 56 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp100.c | 46 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp10b.c | 46 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gv100.c | 44 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mcp77.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.c | 242 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.h | 23 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memgf100.c | 94 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv04.c | 69 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv50.c | 88 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c | 42 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c | 58 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c | 73 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c | 78 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h | 66 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/tu102.c | 58 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.c | 190 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.h | 25 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.c | 181 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.h | 14 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c | 404 +++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.h | 14 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 1869 +++++++++++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h | 355 +++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgf100.c | 424 +++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk104.c | 104 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk20a.c | 73 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm200.c | 187 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm20b.c | 72 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c | 633 ++++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c | 51 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgv100.c | 89 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmmcp77.c | 45 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv04.c | 141 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv41.c | 112 + drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c | 230 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv50.c | 384 +++ drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c | 77 + drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild | 4 + drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c | 279 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c | 191 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h | 23 + drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c | 220 ++ drivers/gpu/drm/nouveau/nvkm/subdev/mxm/priv.h | 16 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild | 15 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c | 175 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h | 19 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c | 232 ++ drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c | 157 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c | 58 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c | 50 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c | 103 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c | 50 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c | 229 ++ drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c | 45 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c | 59 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c | 66 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c | 52 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c | 38 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c | 165 + drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h | 59 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild | 15 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c | 211 ++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc | 94 + .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 | 70 + .../drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h | 1864 +++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4 | 70 + .../drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4.h | 1794 +++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 | 70 + .../drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h | 1730 ++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 | 70 + .../drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h | 1867 +++++++++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc | 150 + .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc | 393 +++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc | 84 + .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc | 544 ++++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc | 272 ++ .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc | 447 +++ drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h | 53 + .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc | 57 + .../gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc | 63 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c | 76 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf119.c | 54 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c | 134 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c | 113 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c | 55 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c | 230 ++ drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm107.c | 56 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm200.c | 81 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c | 248 ++ drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp102.c | 58 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp10b.c | 107 + drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c | 289 ++ drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c | 204 ++ drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h | 72 + .../gpu/drm/nouveau/nvkm/subdev/privring/Kbuild | 7 + .../gpu/drm/nouveau/nvkm/subdev/privring/gf100.c | 122 + .../gpu/drm/nouveau/nvkm/subdev/privring/gf117.c | 47 + .../gpu/drm/nouveau/nvkm/subdev/privring/gk104.c | 125 + .../gpu/drm/nouveau/nvkm/subdev/privring/gk20a.c | 85 + .../gpu/drm/nouveau/nvkm/subdev/privring/gm200.c | 36 + .../gpu/drm/nouveau/nvkm/subdev/privring/gp10b.c | 55 + .../gpu/drm/nouveau/nvkm/subdev/privring/priv.h | 8 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild | 18 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c | 455 +++ drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c | 279 ++ drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c | 52 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c | 110 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c | 116 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c | 247 ++ drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.c | 58 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.h | 35 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c | 154 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.c | 133 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.h | 49 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c | 75 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm200.c | 39 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gp100.c | 56 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c | 75 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c | 127 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c | 204 ++ drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c | 176 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h | 137 + drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c | 253 ++ drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild | 6 + drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c | 198 ++ drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c | 40 + drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c | 152 + drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv40.c | 89 + drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv41.c | 86 + drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h | 27 + .../gpu/drm/nouveau/nvkm/subdev/timer/regsnv04.h | 8 + drivers/gpu/drm/nouveau/nvkm/subdev/top/Kbuild | 4 + drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c | 158 + drivers/gpu/drm/nouveau/nvkm/subdev/top/ga100.c | 108 + drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c | 119 + drivers/gpu/drm/nouveau/nvkm/subdev/top/priv.h | 15 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild | 9 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c | 328 ++ drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c | 71 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf117.c | 61 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c | 141 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c | 186 ++ drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.h | 44 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/gm20b.c | 93 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c | 98 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c | 45 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h | 31 + 790 files changed, 146293 insertions(+) create mode 100644 drivers/gpu/drm/nouveau/nvkm/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/client.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/engine.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/enum.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/event.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/firmware.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/ioctl.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/memory.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/mm.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/notify.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/object.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/oproxy.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/option.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/ramht.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/core/subdev.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/ce/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/device/user.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/gf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/user.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/usergv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/falcon.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/changf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/changk104.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv04.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv17.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifog84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifonv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifotu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/regsnv04.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/user.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/usergv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/fifo/usertu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxtu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gp104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gp107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gp108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv15.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv17.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv44.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msenc/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mspdec/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/mspdec/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msppp/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msppp/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msppp/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/mcp89.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/msvld/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvenc/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvenc/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/nvenc/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/gf108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/gf117.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/gt200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/vic/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/cmdq.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/msgq.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/falcon/v1.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/nvfw/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/nvfw/flcn.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/nvfw/fw.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/nvfw/hs.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bar/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/bus/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf108.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv11.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv17.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mc/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mcp77.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memgf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/tu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgv100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmmcp77.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv41.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/mxm/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4 create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp102.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf117.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/gp10b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/privring/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm200.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gp100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv41.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/timer/regsnv04.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/top/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/top/ga100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/top/priv.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf117.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.h create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gm20b.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h (limited to 'drivers/gpu/drm/nouveau/nvkm') diff --git a/drivers/gpu/drm/nouveau/nvkm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/Kbuild new file mode 100644 index 000000000..db3ade125 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/Kbuild @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +include $(src)/nvkm/core/Kbuild +include $(src)/nvkm/nvfw/Kbuild +include $(src)/nvkm/falcon/Kbuild +include $(src)/nvkm/subdev/Kbuild +include $(src)/nvkm/engine/Kbuild diff --git a/drivers/gpu/drm/nouveau/nvkm/core/Kbuild b/drivers/gpu/drm/nouveau/nvkm/core/Kbuild new file mode 100644 index 000000000..2b471ab58 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/Kbuild @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT +nvkm-y := nvkm/core/client.o +nvkm-y += nvkm/core/engine.o +nvkm-y += nvkm/core/enum.o +nvkm-y += nvkm/core/event.o +nvkm-y += nvkm/core/firmware.o +nvkm-y += nvkm/core/gpuobj.o +nvkm-y += nvkm/core/ioctl.o +nvkm-y += nvkm/core/memory.o +nvkm-y += nvkm/core/mm.o +nvkm-y += nvkm/core/notify.o +nvkm-y += nvkm/core/object.o +nvkm-y += nvkm/core/oproxy.o +nvkm-y += nvkm/core/option.o +nvkm-y += nvkm/core/ramht.o +nvkm-y += nvkm/core/subdev.o diff --git a/drivers/gpu/drm/nouveau/nvkm/core/client.c b/drivers/gpu/drm/nouveau/nvkm/core/client.c new file mode 100644 index 000000000..0c8c55c73 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/client.c @@ -0,0 +1,307 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include + +static int +nvkm_uclient_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + union { + struct nvif_client_v0 v0; + } *args = argv; + struct nvkm_client *client; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))){ + args->v0.name[sizeof(args->v0.name) - 1] = 0; + ret = nvkm_client_new(args->v0.name, args->v0.device, NULL, + NULL, oclass->client->ntfy, &client); + if (ret) + return ret; + } else + return ret; + + client->object.client = oclass->client; + client->object.handle = oclass->handle; + client->object.route = oclass->route; + client->object.token = oclass->token; + client->object.object = oclass->object; + client->debug = oclass->client->debug; + *pobject = &client->object; + return 0; +} + +static const struct nvkm_sclass +nvkm_uclient_sclass = { + .oclass = NVIF_CLASS_CLIENT, + .minver = 0, + .maxver = 0, + .ctor = nvkm_uclient_new, +}; + +struct nvkm_client_notify { + struct nvkm_client *client; + struct nvkm_notify n; + u8 version; + u8 size; + union { + struct nvif_notify_rep_v0 v0; + } rep; +}; + +static int +nvkm_client_notify(struct nvkm_notify *n) +{ + struct nvkm_client_notify *notify = container_of(n, typeof(*notify), n); + struct nvkm_client *client = notify->client; + return client->ntfy(¬ify->rep, notify->size, n->data, n->size); +} + +int +nvkm_client_notify_put(struct nvkm_client *client, int index) +{ + if (index < ARRAY_SIZE(client->notify)) { + if (client->notify[index]) { + nvkm_notify_put(&client->notify[index]->n); + return 0; + } + } + return -ENOENT; +} + +int +nvkm_client_notify_get(struct nvkm_client *client, int index) +{ + if (index < ARRAY_SIZE(client->notify)) { + if (client->notify[index]) { + nvkm_notify_get(&client->notify[index]->n); + return 0; + } + } + return -ENOENT; +} + +int +nvkm_client_notify_del(struct nvkm_client *client, int index) +{ + if (index < ARRAY_SIZE(client->notify)) { + if (client->notify[index]) { + nvkm_notify_fini(&client->notify[index]->n); + kfree(client->notify[index]); + client->notify[index] = NULL; + return 0; + } + } + return -ENOENT; +} + +int +nvkm_client_notify_new(struct nvkm_object *object, + struct nvkm_event *event, void *data, u32 size) +{ + struct nvkm_client *client = object->client; + struct nvkm_client_notify *notify; + union { + struct nvif_notify_req_v0 v0; + } *req = data; + u8 index, reply; + int ret = -ENOSYS; + + for (index = 0; index < ARRAY_SIZE(client->notify); index++) { + if (!client->notify[index]) + break; + } + + if (index == ARRAY_SIZE(client->notify)) + return -ENOSPC; + + notify = kzalloc(sizeof(*notify), GFP_KERNEL); + if (!notify) + return -ENOMEM; + + nvif_ioctl(object, "notify new size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, true))) { + nvif_ioctl(object, "notify new vers %d reply %d route %02x " + "token %llx\n", req->v0.version, + req->v0.reply, req->v0.route, req->v0.token); + notify->version = req->v0.version; + notify->size = sizeof(notify->rep.v0); + notify->rep.v0.version = req->v0.version; + notify->rep.v0.route = req->v0.route; + notify->rep.v0.token = req->v0.token; + reply = req->v0.reply; + } + + if (ret == 0) { + ret = nvkm_notify_init(object, event, nvkm_client_notify, + false, data, size, reply, ¬ify->n); + if (ret == 0) { + client->notify[index] = notify; + notify->client = client; + return index; + } + } + + kfree(notify); + return ret; +} + +static const struct nvkm_object_func nvkm_client; +struct nvkm_client * +nvkm_client_search(struct nvkm_client *client, u64 handle) +{ + struct nvkm_object *object; + + object = nvkm_object_search(client, handle, &nvkm_client); + if (IS_ERR(object)) + return (void *)object; + + return nvkm_client(object); +} + +static int +nvkm_client_mthd_devlist(struct nvkm_client *client, void *data, u32 size) +{ + union { + struct nvif_client_devlist_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(&client->object, "client devlist size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(&client->object, "client devlist vers %d count %d\n", + args->v0.version, args->v0.count); + if (size == sizeof(args->v0.device[0]) * args->v0.count) { + ret = nvkm_device_list(args->v0.device, args->v0.count); + if (ret >= 0) { + args->v0.count = ret; + ret = 0; + } + } else { + ret = -EINVAL; + } + } + + return ret; +} + +static int +nvkm_client_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(object); + switch (mthd) { + case NVIF_CLIENT_V0_DEVLIST: + return nvkm_client_mthd_devlist(client, data, size); + default: + break; + } + return -EINVAL; +} + +static int +nvkm_client_child_new(const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + return oclass->base.ctor(oclass, data, size, pobject); +} + +static int +nvkm_client_child_get(struct nvkm_object *object, int index, + struct nvkm_oclass *oclass) +{ + const struct nvkm_sclass *sclass; + + switch (index) { + case 0: sclass = &nvkm_uclient_sclass; break; + case 1: sclass = &nvkm_udevice_sclass; break; + default: + return -EINVAL; + } + + oclass->ctor = nvkm_client_child_new; + oclass->base = *sclass; + return 0; +} + +static int +nvkm_client_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_client *client = nvkm_client(object); + const char *name[2] = { "fini", "suspend" }; + int i; + nvif_debug(object, "%s notify\n", name[suspend]); + for (i = 0; i < ARRAY_SIZE(client->notify); i++) + nvkm_client_notify_put(client, i); + return 0; +} + +static void * +nvkm_client_dtor(struct nvkm_object *object) +{ + struct nvkm_client *client = nvkm_client(object); + int i; + for (i = 0; i < ARRAY_SIZE(client->notify); i++) + nvkm_client_notify_del(client, i); + return client; +} + +static const struct nvkm_object_func +nvkm_client = { + .dtor = nvkm_client_dtor, + .fini = nvkm_client_fini, + .mthd = nvkm_client_mthd, + .sclass = nvkm_client_child_get, +}; + +int +nvkm_client_new(const char *name, u64 device, const char *cfg, + const char *dbg, + int (*ntfy)(const void *, u32, const void *, u32), + struct nvkm_client **pclient) +{ + struct nvkm_oclass oclass = { .base = nvkm_uclient_sclass }; + struct nvkm_client *client; + + if (!(client = *pclient = kzalloc(sizeof(*client), GFP_KERNEL))) + return -ENOMEM; + oclass.client = client; + + nvkm_object_ctor(&nvkm_client, &oclass, &client->object); + snprintf(client->name, sizeof(client->name), "%s", name); + client->device = device; + client->debug = nvkm_dbgopt(dbg, "CLIENT"); + client->objroot = RB_ROOT; + client->ntfy = ntfy; + INIT_LIST_HEAD(&client->umem); + spin_lock_init(&client->lock); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/engine.c b/drivers/gpu/drm/nouveau/nvkm/core/engine.c new file mode 100644 index 000000000..e41a39ae1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/engine.c @@ -0,0 +1,204 @@ +/* + * 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 +#include +#include + +#include + +bool +nvkm_engine_chsw_load(struct nvkm_engine *engine) +{ + if (engine->func->chsw_load) + return engine->func->chsw_load(engine); + return false; +} + +void +nvkm_engine_unref(struct nvkm_engine **pengine) +{ + struct nvkm_engine *engine = *pengine; + if (engine) { + if (refcount_dec_and_mutex_lock(&engine->use.refcount, &engine->use.mutex)) { + nvkm_subdev_fini(&engine->subdev, false); + engine->use.enabled = false; + mutex_unlock(&engine->use.mutex); + } + *pengine = NULL; + } +} + +struct nvkm_engine * +nvkm_engine_ref(struct nvkm_engine *engine) +{ + int ret; + if (engine) { + if (!refcount_inc_not_zero(&engine->use.refcount)) { + mutex_lock(&engine->use.mutex); + if (!refcount_inc_not_zero(&engine->use.refcount)) { + engine->use.enabled = true; + if ((ret = nvkm_subdev_init(&engine->subdev))) { + engine->use.enabled = false; + mutex_unlock(&engine->use.mutex); + return ERR_PTR(ret); + } + refcount_set(&engine->use.refcount, 1); + } + mutex_unlock(&engine->use.mutex); + } + } + return engine; +} + +void +nvkm_engine_tile(struct nvkm_engine *engine, int region) +{ + struct nvkm_fb *fb = engine->subdev.device->fb; + if (engine->func->tile) + engine->func->tile(engine, region, &fb->tile.region[region]); +} + +static void +nvkm_engine_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_engine *engine = nvkm_engine(subdev); + if (engine->func->intr) + engine->func->intr(engine); +} + +static int +nvkm_engine_info(struct nvkm_subdev *subdev, u64 mthd, u64 *data) +{ + struct nvkm_engine *engine = nvkm_engine(subdev); + if (engine->func->info) { + if (!IS_ERR((engine = nvkm_engine_ref(engine)))) { + int ret = engine->func->info(engine, mthd, data); + nvkm_engine_unref(&engine); + return ret; + } + return PTR_ERR(engine); + } + return -ENOSYS; +} + +static int +nvkm_engine_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_engine *engine = nvkm_engine(subdev); + if (engine->func->fini) + return engine->func->fini(engine, suspend); + return 0; +} + +static int +nvkm_engine_init(struct nvkm_subdev *subdev) +{ + struct nvkm_engine *engine = nvkm_engine(subdev); + struct nvkm_fb *fb = subdev->device->fb; + int ret = 0, i; + s64 time; + + if (!engine->use.enabled) { + nvkm_trace(subdev, "init skipped, engine has no users\n"); + return ret; + } + + if (engine->func->oneinit && !engine->subdev.oneinit) { + nvkm_trace(subdev, "one-time init running...\n"); + time = ktime_to_us(ktime_get()); + ret = engine->func->oneinit(engine); + if (ret) { + nvkm_trace(subdev, "one-time init failed, %d\n", ret); + return ret; + } + + engine->subdev.oneinit = true; + time = ktime_to_us(ktime_get()) - time; + nvkm_trace(subdev, "one-time init completed in %lldus\n", time); + } + + if (engine->func->init) + ret = engine->func->init(engine); + + for (i = 0; fb && i < fb->tile.regions; i++) + nvkm_engine_tile(engine, i); + return ret; +} + +static int +nvkm_engine_preinit(struct nvkm_subdev *subdev) +{ + struct nvkm_engine *engine = nvkm_engine(subdev); + if (engine->func->preinit) + engine->func->preinit(engine); + return 0; +} + +static void * +nvkm_engine_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_engine *engine = nvkm_engine(subdev); + if (engine->func->dtor) + return engine->func->dtor(engine); + mutex_destroy(&engine->use.mutex); + return engine; +} + +const struct nvkm_subdev_func +nvkm_engine = { + .dtor = nvkm_engine_dtor, + .preinit = nvkm_engine_preinit, + .init = nvkm_engine_init, + .fini = nvkm_engine_fini, + .info = nvkm_engine_info, + .intr = nvkm_engine_intr, +}; + +int +nvkm_engine_ctor(const struct nvkm_engine_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool enable, struct nvkm_engine *engine) +{ + nvkm_subdev_ctor(&nvkm_engine, device, type, inst, &engine->subdev); + engine->func = func; + refcount_set(&engine->use.refcount, 0); + mutex_init(&engine->use.mutex); + + if (!nvkm_boolopt(device->cfgopt, engine->subdev.name, enable)) { + nvkm_debug(&engine->subdev, "disabled\n"); + return -ENODEV; + } + + spin_lock_init(&engine->lock); + return 0; +} + +int +nvkm_engine_new_(const struct nvkm_engine_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool enable, + struct nvkm_engine **pengine) +{ + if (!(*pengine = kzalloc(sizeof(**pengine), GFP_KERNEL))) + return -ENOMEM; + return nvkm_engine_ctor(func, device, type, inst, enable, *pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/enum.c b/drivers/gpu/drm/nouveau/nvkm/core/enum.c new file mode 100644 index 000000000..b9581feb2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/enum.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Nouveau Project + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include + +const struct nvkm_enum * +nvkm_enum_find(const struct nvkm_enum *en, u32 value) +{ + while (en->name) { + if (en->value == value) + return en; + en++; + } + + return NULL; +} + +void +nvkm_snprintbf(char *data, int size, const struct nvkm_bitfield *bf, u32 value) +{ + bool space = false; + while (size >= 1 && bf->name) { + if (value & bf->mask) { + int this = snprintf(data, size, "%s%s", + space ? " " : "", bf->name); + size -= this; + data += this; + space = true; + } + bf++; + } + data[0] = '\0'; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/event.c b/drivers/gpu/drm/nouveau/nvkm/core/event.c new file mode 100644 index 000000000..006618d77 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/event.c @@ -0,0 +1,100 @@ +/* + * Copyright 2013-2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +void +nvkm_event_put(struct nvkm_event *event, u32 types, int index) +{ + assert_spin_locked(&event->refs_lock); + while (types) { + int type = __ffs(types); types &= ~(1 << type); + if (--event->refs[index * event->types_nr + type] == 0) { + if (event->func->fini) + event->func->fini(event, 1 << type, index); + } + } +} + +void +nvkm_event_get(struct nvkm_event *event, u32 types, int index) +{ + assert_spin_locked(&event->refs_lock); + while (types) { + int type = __ffs(types); types &= ~(1 << type); + if (++event->refs[index * event->types_nr + type] == 1) { + if (event->func->init) + event->func->init(event, 1 << type, index); + } + } +} + +void +nvkm_event_send(struct nvkm_event *event, u32 types, int index, + void *data, u32 size) +{ + struct nvkm_notify *notify; + unsigned long flags; + + if (!event->refs || WARN_ON(index >= event->index_nr)) + return; + + spin_lock_irqsave(&event->list_lock, flags); + list_for_each_entry(notify, &event->list, head) { + if (notify->index == index && (notify->types & types)) { + if (event->func->send) { + event->func->send(data, size, notify); + continue; + } + nvkm_notify_send(notify, data, size); + } + } + spin_unlock_irqrestore(&event->list_lock, flags); +} + +void +nvkm_event_fini(struct nvkm_event *event) +{ + if (event->refs) { + kfree(event->refs); + event->refs = NULL; + } +} + +int +nvkm_event_init(const struct nvkm_event_func *func, int types_nr, int index_nr, + struct nvkm_event *event) +{ + event->refs = kzalloc(array3_size(index_nr, types_nr, + sizeof(*event->refs)), + GFP_KERNEL); + if (!event->refs) + return -ENOMEM; + + event->func = func; + event->types_nr = types_nr; + event->index_nr = index_nr; + spin_lock_init(&event->refs_lock); + spin_lock_init(&event->list_lock); + INIT_LIST_HEAD(&event->list); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c new file mode 100644 index 000000000..ca1f8463c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include + +int +nvkm_firmware_load_name(const struct nvkm_subdev *subdev, const char *base, + const char *name, int ver, const struct firmware **pfw) +{ + char path[64]; + int ret; + + snprintf(path, sizeof(path), "%s%s", base, name); + ret = nvkm_firmware_get(subdev, path, ver, pfw); + if (ret < 0) + return ret; + + return 0; +} + +int +nvkm_firmware_load_blob(const struct nvkm_subdev *subdev, const char *base, + const char *name, int ver, struct nvkm_blob *blob) +{ + const struct firmware *fw; + int ret; + + ret = nvkm_firmware_load_name(subdev, base, name, ver, &fw); + if (ret == 0) { + blob->data = kmemdup(fw->data, fw->size, GFP_KERNEL); + blob->size = fw->size; + nvkm_firmware_put(fw); + if (!blob->data) + return -ENOMEM; + } + + return ret; +} + +/** + * nvkm_firmware_get - load firmware from the official nvidia/chip/ directory + * @subdev: subdevice that will use that firmware + * @fwname: name of firmware file to load + * @ver: firmware version to load + * @fw: firmware structure to load to + * + * Use this function to load firmware files in the form nvidia/chip/fwname.bin. + * Firmware files released by NVIDIA will always follow this format. + */ +int +nvkm_firmware_get(const struct nvkm_subdev *subdev, const char *fwname, int ver, + const struct firmware **fw) +{ + struct nvkm_device *device = subdev->device; + char f[64]; + char cname[16]; + int i; + + /* Convert device name to lowercase */ + strncpy(cname, device->chip->name, sizeof(cname)); + cname[sizeof(cname) - 1] = '\0'; + i = strlen(cname); + while (i) { + --i; + cname[i] = tolower(cname[i]); + } + + if (ver != 0) + snprintf(f, sizeof(f), "nvidia/%s/%s-%d.bin", cname, fwname, ver); + else + snprintf(f, sizeof(f), "nvidia/%s/%s.bin", cname, fwname); + + if (!firmware_request_nowarn(fw, f, device->dev)) { + nvkm_debug(subdev, "firmware \"%s\" loaded - %zu byte(s)\n", + f, (*fw)->size); + return 0; + } + + nvkm_debug(subdev, "firmware \"%s\" unavailable\n", f); + return -ENOENT; +} + +/* + * nvkm_firmware_put - release firmware loaded with nvkm_firmware_get + */ +void +nvkm_firmware_put(const struct firmware *fw) +{ + release_firmware(fw); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c b/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c new file mode 100644 index 000000000..d6de2b3ed --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c @@ -0,0 +1,278 @@ +/* + * 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 +#include + +#include +#include +#include + +/* fast-path, where backend is able to provide direct pointer to memory */ +static u32 +nvkm_gpuobj_rd32_fast(struct nvkm_gpuobj *gpuobj, u32 offset) +{ + return ioread32_native(gpuobj->map + offset); +} + +static void +nvkm_gpuobj_wr32_fast(struct nvkm_gpuobj *gpuobj, u32 offset, u32 data) +{ + iowrite32_native(data, gpuobj->map + offset); +} + +/* accessor functions for gpuobjs allocated directly from instmem */ +static int +nvkm_gpuobj_heap_map(struct nvkm_gpuobj *gpuobj, u64 offset, + struct nvkm_vmm *vmm, struct nvkm_vma *vma, + void *argv, u32 argc) +{ + return nvkm_memory_map(gpuobj->memory, offset, vmm, vma, argv, argc); +} + +static u32 +nvkm_gpuobj_heap_rd32(struct nvkm_gpuobj *gpuobj, u32 offset) +{ + return nvkm_ro32(gpuobj->memory, offset); +} + +static void +nvkm_gpuobj_heap_wr32(struct nvkm_gpuobj *gpuobj, u32 offset, u32 data) +{ + nvkm_wo32(gpuobj->memory, offset, data); +} + +static const struct nvkm_gpuobj_func nvkm_gpuobj_heap; +static void +nvkm_gpuobj_heap_release(struct nvkm_gpuobj *gpuobj) +{ + gpuobj->func = &nvkm_gpuobj_heap; + nvkm_done(gpuobj->memory); +} + +static const struct nvkm_gpuobj_func +nvkm_gpuobj_heap_fast = { + .release = nvkm_gpuobj_heap_release, + .rd32 = nvkm_gpuobj_rd32_fast, + .wr32 = nvkm_gpuobj_wr32_fast, + .map = nvkm_gpuobj_heap_map, +}; + +static const struct nvkm_gpuobj_func +nvkm_gpuobj_heap_slow = { + .release = nvkm_gpuobj_heap_release, + .rd32 = nvkm_gpuobj_heap_rd32, + .wr32 = nvkm_gpuobj_heap_wr32, + .map = nvkm_gpuobj_heap_map, +}; + +static void * +nvkm_gpuobj_heap_acquire(struct nvkm_gpuobj *gpuobj) +{ + gpuobj->map = nvkm_kmap(gpuobj->memory); + if (likely(gpuobj->map)) + gpuobj->func = &nvkm_gpuobj_heap_fast; + else + gpuobj->func = &nvkm_gpuobj_heap_slow; + return gpuobj->map; +} + +static const struct nvkm_gpuobj_func +nvkm_gpuobj_heap = { + .acquire = nvkm_gpuobj_heap_acquire, + .map = nvkm_gpuobj_heap_map, +}; + +/* accessor functions for gpuobjs sub-allocated from a parent gpuobj */ +static int +nvkm_gpuobj_map(struct nvkm_gpuobj *gpuobj, u64 offset, + struct nvkm_vmm *vmm, struct nvkm_vma *vma, + void *argv, u32 argc) +{ + return nvkm_memory_map(gpuobj->parent, gpuobj->node->offset + offset, + vmm, vma, argv, argc); +} + +static u32 +nvkm_gpuobj_rd32(struct nvkm_gpuobj *gpuobj, u32 offset) +{ + return nvkm_ro32(gpuobj->parent, gpuobj->node->offset + offset); +} + +static void +nvkm_gpuobj_wr32(struct nvkm_gpuobj *gpuobj, u32 offset, u32 data) +{ + nvkm_wo32(gpuobj->parent, gpuobj->node->offset + offset, data); +} + +static const struct nvkm_gpuobj_func nvkm_gpuobj_func; +static void +nvkm_gpuobj_release(struct nvkm_gpuobj *gpuobj) +{ + gpuobj->func = &nvkm_gpuobj_func; + nvkm_done(gpuobj->parent); +} + +static const struct nvkm_gpuobj_func +nvkm_gpuobj_fast = { + .release = nvkm_gpuobj_release, + .rd32 = nvkm_gpuobj_rd32_fast, + .wr32 = nvkm_gpuobj_wr32_fast, + .map = nvkm_gpuobj_map, +}; + +static const struct nvkm_gpuobj_func +nvkm_gpuobj_slow = { + .release = nvkm_gpuobj_release, + .rd32 = nvkm_gpuobj_rd32, + .wr32 = nvkm_gpuobj_wr32, + .map = nvkm_gpuobj_map, +}; + +static void * +nvkm_gpuobj_acquire(struct nvkm_gpuobj *gpuobj) +{ + gpuobj->map = nvkm_kmap(gpuobj->parent); + if (likely(gpuobj->map)) { + gpuobj->map = (u8 *)gpuobj->map + gpuobj->node->offset; + gpuobj->func = &nvkm_gpuobj_fast; + } else { + gpuobj->func = &nvkm_gpuobj_slow; + } + return gpuobj->map; +} + +static const struct nvkm_gpuobj_func +nvkm_gpuobj_func = { + .acquire = nvkm_gpuobj_acquire, + .map = nvkm_gpuobj_map, +}; + +static int +nvkm_gpuobj_ctor(struct nvkm_device *device, u32 size, int align, bool zero, + struct nvkm_gpuobj *parent, struct nvkm_gpuobj *gpuobj) +{ + u32 offset; + int ret; + + if (parent) { + if (align >= 0) { + ret = nvkm_mm_head(&parent->heap, 0, 1, size, size, + max(align, 1), &gpuobj->node); + } else { + ret = nvkm_mm_tail(&parent->heap, 0, 1, size, size, + -align, &gpuobj->node); + } + if (ret) + return ret; + + gpuobj->parent = parent; + gpuobj->func = &nvkm_gpuobj_func; + gpuobj->addr = parent->addr + gpuobj->node->offset; + gpuobj->size = gpuobj->node->length; + + if (zero) { + nvkm_kmap(gpuobj); + for (offset = 0; offset < gpuobj->size; offset += 4) + nvkm_wo32(gpuobj, offset, 0x00000000); + nvkm_done(gpuobj); + } + } else { + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, size, + abs(align), zero, &gpuobj->memory); + if (ret) + return ret; + + gpuobj->func = &nvkm_gpuobj_heap; + gpuobj->addr = nvkm_memory_addr(gpuobj->memory); + gpuobj->size = nvkm_memory_size(gpuobj->memory); + } + + return nvkm_mm_init(&gpuobj->heap, 0, 0, gpuobj->size, 1); +} + +void +nvkm_gpuobj_del(struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_gpuobj *gpuobj = *pgpuobj; + if (gpuobj) { + if (gpuobj->parent) + nvkm_mm_free(&gpuobj->parent->heap, &gpuobj->node); + nvkm_mm_fini(&gpuobj->heap); + nvkm_memory_unref(&gpuobj->memory); + kfree(*pgpuobj); + *pgpuobj = NULL; + } +} + +int +nvkm_gpuobj_new(struct nvkm_device *device, u32 size, int align, bool zero, + struct nvkm_gpuobj *parent, struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_gpuobj *gpuobj; + int ret; + + if (!(gpuobj = *pgpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL))) + return -ENOMEM; + + ret = nvkm_gpuobj_ctor(device, size, align, zero, parent, gpuobj); + if (ret) + nvkm_gpuobj_del(pgpuobj); + return ret; +} + +/* the below is basically only here to support sharing the paged dma object + * for PCI(E)GART on <=nv4x chipsets, and should *not* be expected to work + * anywhere else. + */ + +int +nvkm_gpuobj_wrap(struct nvkm_memory *memory, struct nvkm_gpuobj **pgpuobj) +{ + if (!(*pgpuobj = kzalloc(sizeof(**pgpuobj), GFP_KERNEL))) + return -ENOMEM; + + (*pgpuobj)->addr = nvkm_memory_addr(memory); + (*pgpuobj)->size = nvkm_memory_size(memory); + return 0; +} + +void +nvkm_gpuobj_memcpy_to(struct nvkm_gpuobj *dst, u32 dstoffset, void *src, + u32 length) +{ + int i; + + for (i = 0; i < length; i += 4) + nvkm_wo32(dst, dstoffset + i, *(u32 *)(src + i)); +} + +void +nvkm_gpuobj_memcpy_from(void *dst, struct nvkm_gpuobj *src, u32 srcoffset, + u32 length) +{ + int i; + + for (i = 0; i < length; i += 4) + ((u32 *)src)[i / 4] = nvkm_ro32(src, srcoffset + i); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c b/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c new file mode 100644 index 000000000..45f920da8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c @@ -0,0 +1,459 @@ +/* + * Copyright 2014 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 +#include +#include +#include + +#include +#include + +static int +nvkm_ioctl_nop(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_nop_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "nop size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "nop vers %lld\n", args->v0.version); + args->v0.version = NVIF_VERSION_LATEST; + } + + return ret; +} + +static int +nvkm_ioctl_sclass(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_sclass_v0 v0; + } *args = data; + struct nvkm_oclass oclass = { .client = client }; + int ret = -ENOSYS, i = 0; + + nvif_ioctl(object, "sclass size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "sclass vers %d count %d\n", + args->v0.version, args->v0.count); + if (size != args->v0.count * sizeof(args->v0.oclass[0])) + return -EINVAL; + + while (object->func->sclass && + object->func->sclass(object, i, &oclass) >= 0) { + if (i < args->v0.count) { + args->v0.oclass[i].oclass = oclass.base.oclass; + args->v0.oclass[i].minver = oclass.base.minver; + args->v0.oclass[i].maxver = oclass.base.maxver; + } + i++; + } + + args->v0.count = i; + } + + return ret; +} + +static int +nvkm_ioctl_new(struct nvkm_client *client, + struct nvkm_object *parent, void *data, u32 size) +{ + union { + struct nvif_ioctl_new_v0 v0; + } *args = data; + struct nvkm_object *object = NULL; + struct nvkm_oclass oclass; + int ret = -ENOSYS, i = 0; + + nvif_ioctl(parent, "new size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(parent, "new vers %d handle %08x class %08x " + "route %02x token %llx object %016llx\n", + args->v0.version, args->v0.handle, args->v0.oclass, + args->v0.route, args->v0.token, args->v0.object); + } else + return ret; + + if (!parent->func->sclass) { + nvif_ioctl(parent, "cannot have children\n"); + return -EINVAL; + } + + do { + memset(&oclass, 0x00, sizeof(oclass)); + oclass.handle = args->v0.handle; + oclass.route = args->v0.route; + oclass.token = args->v0.token; + oclass.object = args->v0.object; + oclass.client = client; + oclass.parent = parent; + ret = parent->func->sclass(parent, i++, &oclass); + if (ret) + return ret; + } while (oclass.base.oclass != args->v0.oclass); + + if (oclass.engine) { + oclass.engine = nvkm_engine_ref(oclass.engine); + if (IS_ERR(oclass.engine)) + return PTR_ERR(oclass.engine); + } + + ret = oclass.ctor(&oclass, data, size, &object); + nvkm_engine_unref(&oclass.engine); + if (ret == 0) { + ret = nvkm_object_init(object); + if (ret == 0) { + list_add_tail(&object->head, &parent->tree); + if (nvkm_object_insert(object)) { + client->data = object; + return 0; + } + ret = -EEXIST; + } + nvkm_object_fini(object, false); + } + + nvkm_object_del(&object); + return ret; +} + +static int +nvkm_ioctl_del(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_del none; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "delete size %d\n", size); + if (!(ret = nvif_unvers(ret, &data, &size, args->none))) { + nvif_ioctl(object, "delete\n"); + nvkm_object_fini(object, false); + nvkm_object_del(&object); + } + + return ret ? ret : 1; +} + +static int +nvkm_ioctl_mthd(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_mthd_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "mthd size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "mthd vers %d mthd %02x\n", + args->v0.version, args->v0.method); + ret = nvkm_object_mthd(object, args->v0.method, data, size); + } + + return ret; +} + + +static int +nvkm_ioctl_rd(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_rd_v0 v0; + } *args = data; + union { + u8 b08; + u16 b16; + u32 b32; + } v; + int ret = -ENOSYS; + + nvif_ioctl(object, "rd size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "rd vers %d size %d addr %016llx\n", + args->v0.version, args->v0.size, args->v0.addr); + switch (args->v0.size) { + case 1: + ret = nvkm_object_rd08(object, args->v0.addr, &v.b08); + args->v0.data = v.b08; + break; + case 2: + ret = nvkm_object_rd16(object, args->v0.addr, &v.b16); + args->v0.data = v.b16; + break; + case 4: + ret = nvkm_object_rd32(object, args->v0.addr, &v.b32); + args->v0.data = v.b32; + break; + default: + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int +nvkm_ioctl_wr(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_wr_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "wr size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, + "wr vers %d size %d addr %016llx data %08x\n", + args->v0.version, args->v0.size, args->v0.addr, + args->v0.data); + } else + return ret; + + switch (args->v0.size) { + case 1: return nvkm_object_wr08(object, args->v0.addr, args->v0.data); + case 2: return nvkm_object_wr16(object, args->v0.addr, args->v0.data); + case 4: return nvkm_object_wr32(object, args->v0.addr, args->v0.data); + default: + break; + } + + return -EINVAL; +} + +static int +nvkm_ioctl_map(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_map_v0 v0; + } *args = data; + enum nvkm_object_map type; + int ret = -ENOSYS; + + nvif_ioctl(object, "map size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "map vers %d\n", args->v0.version); + ret = nvkm_object_map(object, data, size, &type, + &args->v0.handle, + &args->v0.length); + if (type == NVKM_OBJECT_MAP_IO) + args->v0.type = NVIF_IOCTL_MAP_V0_IO; + else + args->v0.type = NVIF_IOCTL_MAP_V0_VA; + } + + return ret; +} + +static int +nvkm_ioctl_unmap(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_unmap none; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "unmap size %d\n", size); + if (!(ret = nvif_unvers(ret, &data, &size, args->none))) { + nvif_ioctl(object, "unmap\n"); + ret = nvkm_object_unmap(object); + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_new(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_ntfy_new_v0 v0; + } *args = data; + struct nvkm_event *event; + int ret = -ENOSYS; + + nvif_ioctl(object, "ntfy new size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "ntfy new vers %d event %02x\n", + args->v0.version, args->v0.event); + ret = nvkm_object_ntfy(object, args->v0.event, &event); + if (ret == 0) { + ret = nvkm_client_notify_new(object, event, data, size); + if (ret >= 0) { + args->v0.index = ret; + ret = 0; + } + } + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_del(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_ntfy_del_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "ntfy del size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "ntfy del vers %d index %d\n", + args->v0.version, args->v0.index); + ret = nvkm_client_notify_del(client, args->v0.index); + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_get(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_ntfy_get_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "ntfy get size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "ntfy get vers %d index %d\n", + args->v0.version, args->v0.index); + ret = nvkm_client_notify_get(client, args->v0.index); + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_put(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nvif_ioctl_ntfy_put_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "ntfy put size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "ntfy put vers %d index %d\n", + args->v0.version, args->v0.index); + ret = nvkm_client_notify_put(client, args->v0.index); + } + + return ret; +} + +static struct { + int version; + int (*func)(struct nvkm_client *, struct nvkm_object *, void *, u32); +} +nvkm_ioctl_v0[] = { + { 0x00, nvkm_ioctl_nop }, + { 0x00, nvkm_ioctl_sclass }, + { 0x00, nvkm_ioctl_new }, + { 0x00, nvkm_ioctl_del }, + { 0x00, nvkm_ioctl_mthd }, + { 0x00, nvkm_ioctl_rd }, + { 0x00, nvkm_ioctl_wr }, + { 0x00, nvkm_ioctl_map }, + { 0x00, nvkm_ioctl_unmap }, + { 0x00, nvkm_ioctl_ntfy_new }, + { 0x00, nvkm_ioctl_ntfy_del }, + { 0x00, nvkm_ioctl_ntfy_get }, + { 0x00, nvkm_ioctl_ntfy_put }, +}; + +static int +nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type, + void *data, u32 size, u8 owner, u8 *route, u64 *token) +{ + struct nvkm_object *object; + int ret; + + object = nvkm_object_search(client, handle, NULL); + if (IS_ERR(object)) { + nvif_ioctl(&client->object, "object not found\n"); + return PTR_ERR(object); + } + + if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != object->route) { + nvif_ioctl(&client->object, "route != owner\n"); + return -EACCES; + } + *route = object->route; + *token = object->token; + + if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { + if (nvkm_ioctl_v0[type].version == 0) + ret = nvkm_ioctl_v0[type].func(client, object, data, size); + } + + return ret; +} + +int +nvkm_ioctl(struct nvkm_client *client, void *data, u32 size, void **hack) +{ + struct nvkm_object *object = &client->object; + union { + struct nvif_ioctl_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "size %d\n", size); + + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, + "vers %d type %02x object %016llx owner %02x\n", + args->v0.version, args->v0.type, args->v0.object, + args->v0.owner); + ret = nvkm_ioctl_path(client, args->v0.object, args->v0.type, + data, size, args->v0.owner, + &args->v0.route, &args->v0.token); + } + + if (ret != 1) { + nvif_ioctl(object, "return %d\n", ret); + if (hack) { + *hack = client->data; + client->data = NULL; + } + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/memory.c b/drivers/gpu/drm/nouveau/nvkm/core/memory.c new file mode 100644 index 000000000..c69daac9b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/memory.c @@ -0,0 +1,154 @@ +/* + * Copyright 2015 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 +#include +#include +#include + +void +nvkm_memory_tags_put(struct nvkm_memory *memory, struct nvkm_device *device, + struct nvkm_tags **ptags) +{ + struct nvkm_fb *fb = device->fb; + struct nvkm_tags *tags = *ptags; + if (tags) { + mutex_lock(&fb->tags.mutex); + if (refcount_dec_and_test(&tags->refcount)) { + nvkm_mm_free(&fb->tags.mm, &tags->mn); + kfree(memory->tags); + memory->tags = NULL; + } + mutex_unlock(&fb->tags.mutex); + *ptags = NULL; + } +} + +int +nvkm_memory_tags_get(struct nvkm_memory *memory, struct nvkm_device *device, + u32 nr, void (*clr)(struct nvkm_device *, u32, u32), + struct nvkm_tags **ptags) +{ + struct nvkm_fb *fb = device->fb; + struct nvkm_tags *tags; + + mutex_lock(&fb->tags.mutex); + if ((tags = memory->tags)) { + /* If comptags exist for the memory, but a different amount + * than requested, the buffer is being mapped with settings + * that are incompatible with existing mappings. + */ + if (tags->mn && tags->mn->length != nr) { + mutex_unlock(&fb->tags.mutex); + return -EINVAL; + } + + refcount_inc(&tags->refcount); + mutex_unlock(&fb->tags.mutex); + *ptags = tags; + return 0; + } + + if (!(tags = kmalloc(sizeof(*tags), GFP_KERNEL))) { + mutex_unlock(&fb->tags.mutex); + return -ENOMEM; + } + + if (!nvkm_mm_head(&fb->tags.mm, 0, 1, nr, nr, 1, &tags->mn)) { + if (clr) + clr(device, tags->mn->offset, tags->mn->length); + } else { + /* Failure to allocate HW comptags is not an error, the + * caller should fall back to an uncompressed map. + * + * As memory can be mapped in multiple places, we still + * need to track the allocation failure and ensure that + * any additional mappings remain uncompressed. + * + * This is handled by returning an empty nvkm_tags. + */ + tags->mn = NULL; + } + + refcount_set(&tags->refcount, 1); + *ptags = memory->tags = tags; + mutex_unlock(&fb->tags.mutex); + return 0; +} + +void +nvkm_memory_ctor(const struct nvkm_memory_func *func, + struct nvkm_memory *memory) +{ + memory->func = func; + kref_init(&memory->kref); +} + +static void +nvkm_memory_del(struct kref *kref) +{ + struct nvkm_memory *memory = container_of(kref, typeof(*memory), kref); + if (!WARN_ON(!memory->func)) { + if (memory->func->dtor) + memory = memory->func->dtor(memory); + kfree(memory); + } +} + +void +nvkm_memory_unref(struct nvkm_memory **pmemory) +{ + struct nvkm_memory *memory = *pmemory; + if (memory) { + kref_put(&memory->kref, nvkm_memory_del); + *pmemory = NULL; + } +} + +struct nvkm_memory * +nvkm_memory_ref(struct nvkm_memory *memory) +{ + if (memory) + kref_get(&memory->kref); + return memory; +} + +int +nvkm_memory_new(struct nvkm_device *device, enum nvkm_memory_target target, + u64 size, u32 align, bool zero, + struct nvkm_memory **pmemory) +{ + struct nvkm_instmem *imem = device->imem; + struct nvkm_memory *memory; + int ret; + + if (unlikely(target != NVKM_MEM_TARGET_INST || !imem)) + return -ENOSYS; + + ret = nvkm_instobj_new(imem, size, align, zero, &memory); + if (ret) + return ret; + + *pmemory = memory; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/mm.c b/drivers/gpu/drm/nouveau/nvkm/core/mm.c new file mode 100644 index 000000000..f78a06a6b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/mm.c @@ -0,0 +1,307 @@ +/* + * 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 + +#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ + list_entry((root)->nl_entry.dir, struct nvkm_mm_node, nl_entry) + +void +nvkm_mm_dump(struct nvkm_mm *mm, const char *header) +{ + struct nvkm_mm_node *node; + + pr_err("nvkm: %s\n", header); + pr_err("nvkm: node list:\n"); + list_for_each_entry(node, &mm->nodes, nl_entry) { + pr_err("nvkm: \t%08x %08x %d\n", + node->offset, node->length, node->type); + } + pr_err("nvkm: free list:\n"); + list_for_each_entry(node, &mm->free, fl_entry) { + pr_err("nvkm: \t%08x %08x %d\n", + node->offset, node->length, node->type); + } +} + +void +nvkm_mm_free(struct nvkm_mm *mm, struct nvkm_mm_node **pthis) +{ + struct nvkm_mm_node *this = *pthis; + + if (this) { + struct nvkm_mm_node *prev = node(this, prev); + struct nvkm_mm_node *next = node(this, next); + + if (prev && prev->type == NVKM_MM_TYPE_NONE) { + prev->length += this->length; + list_del(&this->nl_entry); + kfree(this); this = prev; + } + + if (next && next->type == NVKM_MM_TYPE_NONE) { + next->offset = this->offset; + next->length += this->length; + if (this->type == NVKM_MM_TYPE_NONE) + list_del(&this->fl_entry); + list_del(&this->nl_entry); + kfree(this); this = NULL; + } + + if (this && this->type != NVKM_MM_TYPE_NONE) { + list_for_each_entry(prev, &mm->free, fl_entry) { + if (this->offset < prev->offset) + break; + } + + list_add_tail(&this->fl_entry, &prev->fl_entry); + this->type = NVKM_MM_TYPE_NONE; + } + } + + *pthis = NULL; +} + +static struct nvkm_mm_node * +region_head(struct nvkm_mm *mm, struct nvkm_mm_node *a, u32 size) +{ + struct nvkm_mm_node *b; + + if (a->length == size) + return a; + + b = kmalloc(sizeof(*b), GFP_KERNEL); + if (unlikely(b == NULL)) + return NULL; + + b->offset = a->offset; + b->length = size; + b->heap = a->heap; + b->type = a->type; + a->offset += size; + a->length -= size; + list_add_tail(&b->nl_entry, &a->nl_entry); + if (b->type == NVKM_MM_TYPE_NONE) + list_add_tail(&b->fl_entry, &a->fl_entry); + + return b; +} + +int +nvkm_mm_head(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, + u32 align, struct nvkm_mm_node **pnode) +{ + struct nvkm_mm_node *prev, *this, *next; + u32 mask = align - 1; + u32 splitoff; + u32 s, e; + + BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); + + list_for_each_entry(this, &mm->free, fl_entry) { + if (unlikely(heap != NVKM_MM_HEAP_ANY)) { + if (this->heap != heap) + continue; + } + e = this->offset + this->length; + s = this->offset; + + prev = node(this, prev); + if (prev && prev->type != type) + s = roundup(s, mm->block_size); + + next = node(this, next); + if (next && next->type != type) + e = rounddown(e, mm->block_size); + + s = (s + mask) & ~mask; + e &= ~mask; + if (s > e || e - s < size_min) + continue; + + splitoff = s - this->offset; + if (splitoff && !region_head(mm, this, splitoff)) + return -ENOMEM; + + this = region_head(mm, this, min(size_max, e - s)); + if (!this) + return -ENOMEM; + + this->next = NULL; + this->type = type; + list_del(&this->fl_entry); + *pnode = this; + return 0; + } + + return -ENOSPC; +} + +static struct nvkm_mm_node * +region_tail(struct nvkm_mm *mm, struct nvkm_mm_node *a, u32 size) +{ + struct nvkm_mm_node *b; + + if (a->length == size) + return a; + + b = kmalloc(sizeof(*b), GFP_KERNEL); + if (unlikely(b == NULL)) + return NULL; + + a->length -= size; + b->offset = a->offset + a->length; + b->length = size; + b->heap = a->heap; + b->type = a->type; + + list_add(&b->nl_entry, &a->nl_entry); + if (b->type == NVKM_MM_TYPE_NONE) + list_add(&b->fl_entry, &a->fl_entry); + + return b; +} + +int +nvkm_mm_tail(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, + u32 align, struct nvkm_mm_node **pnode) +{ + struct nvkm_mm_node *prev, *this, *next; + u32 mask = align - 1; + + BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); + + list_for_each_entry_reverse(this, &mm->free, fl_entry) { + u32 e = this->offset + this->length; + u32 s = this->offset; + u32 c = 0, a; + if (unlikely(heap != NVKM_MM_HEAP_ANY)) { + if (this->heap != heap) + continue; + } + + prev = node(this, prev); + if (prev && prev->type != type) + s = roundup(s, mm->block_size); + + next = node(this, next); + if (next && next->type != type) { + e = rounddown(e, mm->block_size); + c = next->offset - e; + } + + s = (s + mask) & ~mask; + a = e - s; + if (s > e || a < size_min) + continue; + + a = min(a, size_max); + s = (e - a) & ~mask; + c += (e - s) - a; + + if (c && !region_tail(mm, this, c)) + return -ENOMEM; + + this = region_tail(mm, this, a); + if (!this) + return -ENOMEM; + + this->next = NULL; + this->type = type; + list_del(&this->fl_entry); + *pnode = this; + return 0; + } + + return -ENOSPC; +} + +int +nvkm_mm_init(struct nvkm_mm *mm, u8 heap, u32 offset, u32 length, u32 block) +{ + struct nvkm_mm_node *node, *prev; + u32 next; + + if (nvkm_mm_initialised(mm)) { + prev = list_last_entry(&mm->nodes, typeof(*node), nl_entry); + next = prev->offset + prev->length; + if (next != offset) { + BUG_ON(next > offset); + if (!(node = kzalloc(sizeof(*node), GFP_KERNEL))) + return -ENOMEM; + node->type = NVKM_MM_TYPE_HOLE; + node->offset = next; + node->length = offset - next; + list_add_tail(&node->nl_entry, &mm->nodes); + } + BUG_ON(block != mm->block_size); + } else { + INIT_LIST_HEAD(&mm->nodes); + INIT_LIST_HEAD(&mm->free); + mm->block_size = block; + mm->heap_nodes = 0; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + if (length) { + node->offset = roundup(offset, mm->block_size); + node->length = rounddown(offset + length, mm->block_size); + node->length -= node->offset; + } + + list_add_tail(&node->nl_entry, &mm->nodes); + list_add_tail(&node->fl_entry, &mm->free); + node->heap = heap; + mm->heap_nodes++; + return 0; +} + +int +nvkm_mm_fini(struct nvkm_mm *mm) +{ + struct nvkm_mm_node *node, *temp; + int nodes = 0; + + if (!nvkm_mm_initialised(mm)) + return 0; + + list_for_each_entry(node, &mm->nodes, nl_entry) { + if (node->type != NVKM_MM_TYPE_HOLE) { + if (++nodes > mm->heap_nodes) { + nvkm_mm_dump(mm, "mm not clean!"); + return -EBUSY; + } + } + } + + list_for_each_entry_safe(node, temp, &mm->nodes, nl_entry) { + list_del(&node->nl_entry); + kfree(node); + } + + mm->heap_nodes = 0; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/notify.c b/drivers/gpu/drm/nouveau/nvkm/core/notify.c new file mode 100644 index 000000000..023610d01 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/notify.c @@ -0,0 +1,163 @@ +/* + * Copyright 2014 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 +#include + +static inline void +nvkm_notify_put_locked(struct nvkm_notify *notify) +{ + if (notify->block++ == 0) + nvkm_event_put(notify->event, notify->types, notify->index); +} + +void +nvkm_notify_put(struct nvkm_notify *notify) +{ + struct nvkm_event *event = notify->event; + unsigned long flags; + if (likely(event) && + test_and_clear_bit(NVKM_NOTIFY_USER, ¬ify->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + nvkm_notify_put_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + if (test_bit(NVKM_NOTIFY_WORK, ¬ify->flags)) + flush_work(¬ify->work); + } +} + +static inline void +nvkm_notify_get_locked(struct nvkm_notify *notify) +{ + if (--notify->block == 0) + nvkm_event_get(notify->event, notify->types, notify->index); +} + +void +nvkm_notify_get(struct nvkm_notify *notify) +{ + struct nvkm_event *event = notify->event; + unsigned long flags; + if (likely(event) && + !test_and_set_bit(NVKM_NOTIFY_USER, ¬ify->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + nvkm_notify_get_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + } +} + +static inline void +nvkm_notify_func(struct nvkm_notify *notify) +{ + struct nvkm_event *event = notify->event; + int ret = notify->func(notify); + unsigned long flags; + if ((ret == NVKM_NOTIFY_KEEP) || + !test_and_clear_bit(NVKM_NOTIFY_USER, ¬ify->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + nvkm_notify_get_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + } +} + +static void +nvkm_notify_work(struct work_struct *work) +{ + struct nvkm_notify *notify = container_of(work, typeof(*notify), work); + nvkm_notify_func(notify); +} + +void +nvkm_notify_send(struct nvkm_notify *notify, void *data, u32 size) +{ + struct nvkm_event *event = notify->event; + unsigned long flags; + + assert_spin_locked(&event->list_lock); + BUG_ON(size != notify->size); + + spin_lock_irqsave(&event->refs_lock, flags); + if (notify->block) { + spin_unlock_irqrestore(&event->refs_lock, flags); + return; + } + nvkm_notify_put_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + + if (test_bit(NVKM_NOTIFY_WORK, ¬ify->flags)) { + memcpy((void *)notify->data, data, size); + schedule_work(¬ify->work); + } else { + notify->data = data; + nvkm_notify_func(notify); + notify->data = NULL; + } +} + +void +nvkm_notify_fini(struct nvkm_notify *notify) +{ + unsigned long flags; + if (notify->event) { + nvkm_notify_put(notify); + spin_lock_irqsave(¬ify->event->list_lock, flags); + list_del(¬ify->head); + spin_unlock_irqrestore(¬ify->event->list_lock, flags); + kfree((void *)notify->data); + notify->event = NULL; + } +} + +int +nvkm_notify_init(struct nvkm_object *object, struct nvkm_event *event, + int (*func)(struct nvkm_notify *), bool work, + void *data, u32 size, u32 reply, + struct nvkm_notify *notify) +{ + unsigned long flags; + int ret = -ENODEV; + if ((notify->event = event), event->refs) { + ret = event->func->ctor(object, data, size, notify); + if (ret == 0 && (ret = -EINVAL, notify->size == reply)) { + notify->flags = 0; + notify->block = 1; + notify->func = func; + notify->data = NULL; + if (ret = 0, work) { + INIT_WORK(¬ify->work, nvkm_notify_work); + set_bit(NVKM_NOTIFY_WORK, ¬ify->flags); + notify->data = kmalloc(reply, GFP_KERNEL); + if (!notify->data) + ret = -ENOMEM; + } + } + if (ret == 0) { + spin_lock_irqsave(&event->list_lock, flags); + list_add_tail(¬ify->head, &event->list); + spin_unlock_irqrestore(&event->list_lock, flags); + } + } + if (ret) + notify->event = NULL; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/object.c b/drivers/gpu/drm/nouveau/nvkm/core/object.c new file mode 100644 index 000000000..301a5e5b5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/object.c @@ -0,0 +1,336 @@ +/* + * 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 +#include +#include + +struct nvkm_object * +nvkm_object_search(struct nvkm_client *client, u64 handle, + const struct nvkm_object_func *func) +{ + struct nvkm_object *object; + + if (handle) { + struct rb_node *node = client->objroot.rb_node; + while (node) { + object = rb_entry(node, typeof(*object), node); + if (handle < object->object) + node = node->rb_left; + else + if (handle > object->object) + node = node->rb_right; + else + goto done; + } + return ERR_PTR(-ENOENT); + } else { + object = &client->object; + } + +done: + if (unlikely(func && object->func != func)) + return ERR_PTR(-EINVAL); + return object; +} + +void +nvkm_object_remove(struct nvkm_object *object) +{ + if (!RB_EMPTY_NODE(&object->node)) + rb_erase(&object->node, &object->client->objroot); +} + +bool +nvkm_object_insert(struct nvkm_object *object) +{ + struct rb_node **ptr = &object->client->objroot.rb_node; + struct rb_node *parent = NULL; + + while (*ptr) { + struct nvkm_object *this = rb_entry(*ptr, typeof(*this), node); + parent = *ptr; + if (object->object < this->object) + ptr = &parent->rb_left; + else + if (object->object > this->object) + ptr = &parent->rb_right; + else + return false; + } + + rb_link_node(&object->node, parent, ptr); + rb_insert_color(&object->node, &object->client->objroot); + return true; +} + +int +nvkm_object_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + if (likely(object->func->mthd)) + return object->func->mthd(object, mthd, data, size); + return -ENODEV; +} + +int +nvkm_object_ntfy(struct nvkm_object *object, u32 mthd, + struct nvkm_event **pevent) +{ + if (likely(object->func->ntfy)) + return object->func->ntfy(object, mthd, pevent); + return -ENODEV; +} + +int +nvkm_object_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + if (likely(object->func->map)) + return object->func->map(object, argv, argc, type, addr, size); + return -ENODEV; +} + +int +nvkm_object_unmap(struct nvkm_object *object) +{ + if (likely(object->func->unmap)) + return object->func->unmap(object); + return -ENODEV; +} + +int +nvkm_object_rd08(struct nvkm_object *object, u64 addr, u8 *data) +{ + if (likely(object->func->rd08)) + return object->func->rd08(object, addr, data); + return -ENODEV; +} + +int +nvkm_object_rd16(struct nvkm_object *object, u64 addr, u16 *data) +{ + if (likely(object->func->rd16)) + return object->func->rd16(object, addr, data); + return -ENODEV; +} + +int +nvkm_object_rd32(struct nvkm_object *object, u64 addr, u32 *data) +{ + if (likely(object->func->rd32)) + return object->func->rd32(object, addr, data); + return -ENODEV; +} + +int +nvkm_object_wr08(struct nvkm_object *object, u64 addr, u8 data) +{ + if (likely(object->func->wr08)) + return object->func->wr08(object, addr, data); + return -ENODEV; +} + +int +nvkm_object_wr16(struct nvkm_object *object, u64 addr, u16 data) +{ + if (likely(object->func->wr16)) + return object->func->wr16(object, addr, data); + return -ENODEV; +} + +int +nvkm_object_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + if (likely(object->func->wr32)) + return object->func->wr32(object, addr, data); + return -ENODEV; +} + +int +nvkm_object_bind(struct nvkm_object *object, struct nvkm_gpuobj *gpuobj, + int align, struct nvkm_gpuobj **pgpuobj) +{ + if (object->func->bind) + return object->func->bind(object, gpuobj, align, pgpuobj); + return -ENODEV; +} + +int +nvkm_object_fini(struct nvkm_object *object, bool suspend) +{ + const char *action = suspend ? "suspend" : "fini"; + struct nvkm_object *child; + s64 time; + int ret; + + nvif_debug(object, "%s children...\n", action); + time = ktime_to_us(ktime_get()); + list_for_each_entry(child, &object->tree, head) { + ret = nvkm_object_fini(child, suspend); + if (ret && suspend) + goto fail_child; + } + + nvif_debug(object, "%s running...\n", action); + if (object->func->fini) { + ret = object->func->fini(object, suspend); + if (ret) { + nvif_error(object, "%s failed with %d\n", action, ret); + if (suspend) + goto fail; + } + } + + time = ktime_to_us(ktime_get()) - time; + nvif_debug(object, "%s completed in %lldus\n", action, time); + return 0; + +fail: + if (object->func->init) { + int rret = object->func->init(object); + if (rret) + nvif_fatal(object, "failed to restart, %d\n", rret); + } +fail_child: + list_for_each_entry_continue_reverse(child, &object->tree, head) { + nvkm_object_init(child); + } + return ret; +} + +int +nvkm_object_init(struct nvkm_object *object) +{ + struct nvkm_object *child; + s64 time; + int ret; + + nvif_debug(object, "init running...\n"); + time = ktime_to_us(ktime_get()); + if (object->func->init) { + ret = object->func->init(object); + if (ret) + goto fail; + } + + nvif_debug(object, "init children...\n"); + list_for_each_entry(child, &object->tree, head) { + ret = nvkm_object_init(child); + if (ret) + goto fail_child; + } + + time = ktime_to_us(ktime_get()) - time; + nvif_debug(object, "init completed in %lldus\n", time); + return 0; + +fail_child: + list_for_each_entry_continue_reverse(child, &object->tree, head) + nvkm_object_fini(child, false); +fail: + nvif_error(object, "init failed with %d\n", ret); + if (object->func->fini) + object->func->fini(object, false); + return ret; +} + +void * +nvkm_object_dtor(struct nvkm_object *object) +{ + struct nvkm_object *child, *ctemp; + void *data = object; + s64 time; + + nvif_debug(object, "destroy children...\n"); + time = ktime_to_us(ktime_get()); + list_for_each_entry_safe(child, ctemp, &object->tree, head) { + nvkm_object_del(&child); + } + + nvif_debug(object, "destroy running...\n"); + nvkm_object_unmap(object); + if (object->func->dtor) + data = object->func->dtor(object); + nvkm_engine_unref(&object->engine); + time = ktime_to_us(ktime_get()) - time; + nvif_debug(object, "destroy completed in %lldus...\n", time); + return data; +} + +void +nvkm_object_del(struct nvkm_object **pobject) +{ + struct nvkm_object *object = *pobject; + if (object && !WARN_ON(!object->func)) { + *pobject = nvkm_object_dtor(object); + nvkm_object_remove(object); + list_del(&object->head); + kfree(*pobject); + *pobject = NULL; + } +} + +void +nvkm_object_ctor(const struct nvkm_object_func *func, + const struct nvkm_oclass *oclass, struct nvkm_object *object) +{ + object->func = func; + object->client = oclass->client; + object->engine = nvkm_engine_ref(oclass->engine); + object->oclass = oclass->base.oclass; + object->handle = oclass->handle; + object->route = oclass->route; + object->token = oclass->token; + object->object = oclass->object; + INIT_LIST_HEAD(&object->head); + INIT_LIST_HEAD(&object->tree); + RB_CLEAR_NODE(&object->node); + WARN_ON(IS_ERR(object->engine)); +} + +int +nvkm_object_new_(const struct nvkm_object_func *func, + const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + if (size == 0) { + if (!(*pobject = kzalloc(sizeof(**pobject), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(func, oclass, *pobject); + return 0; + } + return -ENOSYS; +} + +static const struct nvkm_object_func +nvkm_object_func = { +}; + +int +nvkm_object_new(const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + const struct nvkm_object_func *func = + oclass->base.func ? oclass->base.func : &nvkm_object_func; + return nvkm_object_new_(func, oclass, data, size, pobject); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/oproxy.c b/drivers/gpu/drm/nouveau/nvkm/core/oproxy.c new file mode 100644 index 000000000..16299837a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/oproxy.c @@ -0,0 +1,209 @@ +/* + * Copyright 2015 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 + +static int +nvkm_oproxy_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + return nvkm_object_mthd(nvkm_oproxy(object)->object, mthd, data, size); +} + +static int +nvkm_oproxy_ntfy(struct nvkm_object *object, u32 mthd, + struct nvkm_event **pevent) +{ + return nvkm_object_ntfy(nvkm_oproxy(object)->object, mthd, pevent); +} + +static int +nvkm_oproxy_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct nvkm_oproxy *oproxy = nvkm_oproxy(object); + return nvkm_object_map(oproxy->object, argv, argc, type, addr, size); +} + +static int +nvkm_oproxy_unmap(struct nvkm_object *object) +{ + return nvkm_object_unmap(nvkm_oproxy(object)->object); +} + +static int +nvkm_oproxy_rd08(struct nvkm_object *object, u64 addr, u8 *data) +{ + return nvkm_object_rd08(nvkm_oproxy(object)->object, addr, data); +} + +static int +nvkm_oproxy_rd16(struct nvkm_object *object, u64 addr, u16 *data) +{ + return nvkm_object_rd16(nvkm_oproxy(object)->object, addr, data); +} + +static int +nvkm_oproxy_rd32(struct nvkm_object *object, u64 addr, u32 *data) +{ + return nvkm_object_rd32(nvkm_oproxy(object)->object, addr, data); +} + +static int +nvkm_oproxy_wr08(struct nvkm_object *object, u64 addr, u8 data) +{ + return nvkm_object_wr08(nvkm_oproxy(object)->object, addr, data); +} + +static int +nvkm_oproxy_wr16(struct nvkm_object *object, u64 addr, u16 data) +{ + return nvkm_object_wr16(nvkm_oproxy(object)->object, addr, data); +} + +static int +nvkm_oproxy_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + return nvkm_object_wr32(nvkm_oproxy(object)->object, addr, data); +} + +static int +nvkm_oproxy_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + return nvkm_object_bind(nvkm_oproxy(object)->object, + parent, align, pgpuobj); +} + +static int +nvkm_oproxy_sclass(struct nvkm_object *object, int index, + struct nvkm_oclass *oclass) +{ + struct nvkm_oproxy *oproxy = nvkm_oproxy(object); + oclass->parent = oproxy->object; + if (!oproxy->object->func->sclass) + return -ENODEV; + return oproxy->object->func->sclass(oproxy->object, index, oclass); +} + +static int +nvkm_oproxy_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_oproxy *oproxy = nvkm_oproxy(object); + int ret; + + if (oproxy->func->fini[0]) { + ret = oproxy->func->fini[0](oproxy, suspend); + if (ret && suspend) + return ret; + } + + if (oproxy->object->func->fini) { + ret = oproxy->object->func->fini(oproxy->object, suspend); + if (ret && suspend) + return ret; + } + + if (oproxy->func->fini[1]) { + ret = oproxy->func->fini[1](oproxy, suspend); + if (ret && suspend) + return ret; + } + + return 0; +} + +static int +nvkm_oproxy_init(struct nvkm_object *object) +{ + struct nvkm_oproxy *oproxy = nvkm_oproxy(object); + int ret; + + if (oproxy->func->init[0]) { + ret = oproxy->func->init[0](oproxy); + if (ret) + return ret; + } + + if (oproxy->object->func->init) { + ret = oproxy->object->func->init(oproxy->object); + if (ret) + return ret; + } + + if (oproxy->func->init[1]) { + ret = oproxy->func->init[1](oproxy); + if (ret) + return ret; + } + + return 0; +} + +static void * +nvkm_oproxy_dtor(struct nvkm_object *object) +{ + struct nvkm_oproxy *oproxy = nvkm_oproxy(object); + if (oproxy->func->dtor[0]) + oproxy->func->dtor[0](oproxy); + nvkm_object_del(&oproxy->object); + if (oproxy->func->dtor[1]) + oproxy->func->dtor[1](oproxy); + return oproxy; +} + +static const struct nvkm_object_func +nvkm_oproxy_func = { + .dtor = nvkm_oproxy_dtor, + .init = nvkm_oproxy_init, + .fini = nvkm_oproxy_fini, + .mthd = nvkm_oproxy_mthd, + .ntfy = nvkm_oproxy_ntfy, + .map = nvkm_oproxy_map, + .unmap = nvkm_oproxy_unmap, + .rd08 = nvkm_oproxy_rd08, + .rd16 = nvkm_oproxy_rd16, + .rd32 = nvkm_oproxy_rd32, + .wr08 = nvkm_oproxy_wr08, + .wr16 = nvkm_oproxy_wr16, + .wr32 = nvkm_oproxy_wr32, + .bind = nvkm_oproxy_bind, + .sclass = nvkm_oproxy_sclass, +}; + +void +nvkm_oproxy_ctor(const struct nvkm_oproxy_func *func, + const struct nvkm_oclass *oclass, struct nvkm_oproxy *oproxy) +{ + nvkm_object_ctor(&nvkm_oproxy_func, oclass, &oproxy->base); + oproxy->func = func; +} + +int +nvkm_oproxy_new_(const struct nvkm_oproxy_func *func, + const struct nvkm_oclass *oclass, struct nvkm_oproxy **poproxy) +{ + if (!(*poproxy = kzalloc(sizeof(**poproxy), GFP_KERNEL))) + return -ENOMEM; + nvkm_oproxy_ctor(func, oclass, *poproxy); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/option.c b/drivers/gpu/drm/nouveau/nvkm/core/option.c new file mode 100644 index 000000000..3e62cf8cd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/option.c @@ -0,0 +1,139 @@ +/* + * 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 +#include + +const char * +nvkm_stropt(const char *optstr, const char *opt, int *arglen) +{ + while (optstr && *optstr != '\0') { + int len = strcspn(optstr, ",="); + switch (optstr[len]) { + case '=': + if (!strncasecmpz(optstr, opt, len)) { + optstr += len + 1; + *arglen = strcspn(optstr, ",="); + return *arglen ? optstr : NULL; + } + optstr++; + break; + case ',': + optstr++; + break; + default: + break; + } + optstr += len; + } + + return NULL; +} + +bool +nvkm_boolopt(const char *optstr, const char *opt, bool value) +{ + int arglen; + + optstr = nvkm_stropt(optstr, opt, &arglen); + if (optstr) { + if (!strncasecmpz(optstr, "0", arglen) || + !strncasecmpz(optstr, "no", arglen) || + !strncasecmpz(optstr, "off", arglen) || + !strncasecmpz(optstr, "false", arglen)) + value = false; + else + if (!strncasecmpz(optstr, "1", arglen) || + !strncasecmpz(optstr, "yes", arglen) || + !strncasecmpz(optstr, "on", arglen) || + !strncasecmpz(optstr, "true", arglen)) + value = true; + } + + return value; +} + +long +nvkm_longopt(const char *optstr, const char *opt, long value) +{ + long result = value; + int arglen; + char *s; + + optstr = nvkm_stropt(optstr, opt, &arglen); + if (optstr && (s = kstrndup(optstr, arglen, GFP_KERNEL))) { + int ret = kstrtol(s, 0, &value); + if (ret == 0) + result = value; + kfree(s); + } + + return result; +} + +int +nvkm_dbgopt(const char *optstr, const char *sub) +{ + int mode = 1, level = CONFIG_NOUVEAU_DEBUG_DEFAULT; + + while (optstr) { + int len = strcspn(optstr, ",="); + switch (optstr[len]) { + case '=': + if (strncasecmpz(optstr, sub, len)) + mode = 0; + optstr++; + break; + default: + if (mode) { + if (!strncasecmpz(optstr, "fatal", len)) + level = NV_DBG_FATAL; + else if (!strncasecmpz(optstr, "error", len)) + level = NV_DBG_ERROR; + else if (!strncasecmpz(optstr, "warn", len)) + level = NV_DBG_WARN; + else if (!strncasecmpz(optstr, "info", len)) + level = NV_DBG_INFO; + else if (!strncasecmpz(optstr, "debug", len)) + level = NV_DBG_DEBUG; + else if (!strncasecmpz(optstr, "trace", len)) + level = NV_DBG_TRACE; + else if (!strncasecmpz(optstr, "paranoia", len)) + level = NV_DBG_PARANOIA; + else if (!strncasecmpz(optstr, "spam", len)) + level = NV_DBG_SPAM; + } + + if (optstr[len] != '\0') { + optstr++; + mode = 1; + break; + } + + return level; + } + optstr += len; + } + + return level; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/ramht.c b/drivers/gpu/drm/nouveau/nvkm/core/ramht.c new file mode 100644 index 000000000..8162e3d23 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/ramht.c @@ -0,0 +1,162 @@ +/* + * 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. + */ +#include +#include +#include + +static u32 +nvkm_ramht_hash(struct nvkm_ramht *ramht, int chid, u32 handle) +{ + u32 hash = 0; + + while (handle) { + hash ^= (handle & ((1 << ramht->bits) - 1)); + handle >>= ramht->bits; + } + + hash ^= chid << (ramht->bits - 4); + return hash; +} + +struct nvkm_gpuobj * +nvkm_ramht_search(struct nvkm_ramht *ramht, int chid, u32 handle) +{ + u32 co, ho; + + co = ho = nvkm_ramht_hash(ramht, chid, handle); + do { + if (ramht->data[co].chid == chid) { + if (ramht->data[co].handle == handle) + return ramht->data[co].inst; + } + + if (++co >= ramht->size) + co = 0; + } while (co != ho); + + return NULL; +} + +static int +nvkm_ramht_update(struct nvkm_ramht *ramht, int co, struct nvkm_object *object, + int chid, int addr, u32 handle, u32 context) +{ + struct nvkm_ramht_data *data = &ramht->data[co]; + u64 inst = 0x00000040; /* just non-zero for <=g8x fifo ramht */ + int ret; + + nvkm_gpuobj_del(&data->inst); + data->chid = chid; + data->handle = handle; + + if (object) { + ret = nvkm_object_bind(object, ramht->parent, 16, &data->inst); + if (ret) { + if (ret != -ENODEV) { + data->chid = -1; + return ret; + } + data->inst = NULL; + } + + if (data->inst) { + if (ramht->device->card_type >= NV_50) + inst = data->inst->node->offset; + else + inst = data->inst->addr; + } + + if (addr < 0) context |= inst << -addr; + else context |= inst >> addr; + } + + nvkm_kmap(ramht->gpuobj); + nvkm_wo32(ramht->gpuobj, (co << 3) + 0, handle); + nvkm_wo32(ramht->gpuobj, (co << 3) + 4, context); + nvkm_done(ramht->gpuobj); + return co + 1; +} + +void +nvkm_ramht_remove(struct nvkm_ramht *ramht, int cookie) +{ + if (--cookie >= 0) + nvkm_ramht_update(ramht, cookie, NULL, -1, 0, 0, 0); +} + +int +nvkm_ramht_insert(struct nvkm_ramht *ramht, struct nvkm_object *object, + int chid, int addr, u32 handle, u32 context) +{ + u32 co, ho; + + if (nvkm_ramht_search(ramht, chid, handle)) + return -EEXIST; + + co = ho = nvkm_ramht_hash(ramht, chid, handle); + do { + if (ramht->data[co].chid < 0) { + return nvkm_ramht_update(ramht, co, object, chid, + addr, handle, context); + } + + if (++co >= ramht->size) + co = 0; + } while (co != ho); + + return -ENOSPC; +} + +void +nvkm_ramht_del(struct nvkm_ramht **pramht) +{ + struct nvkm_ramht *ramht = *pramht; + if (ramht) { + nvkm_gpuobj_del(&ramht->gpuobj); + vfree(*pramht); + *pramht = NULL; + } +} + +int +nvkm_ramht_new(struct nvkm_device *device, u32 size, u32 align, + struct nvkm_gpuobj *parent, struct nvkm_ramht **pramht) +{ + struct nvkm_ramht *ramht; + int ret, i; + + if (!(ramht = *pramht = vzalloc(struct_size(ramht, data, (size >> 3))))) + return -ENOMEM; + + ramht->device = device; + ramht->parent = parent; + ramht->size = size >> 3; + ramht->bits = order_base_2(ramht->size); + for (i = 0; i < ramht->size; i++) + ramht->data[i].chid = -1; + + ret = nvkm_gpuobj_new(ramht->device, size, align, true, + ramht->parent, &ramht->gpuobj); + if (ret) + nvkm_ramht_del(pramht); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/subdev.c b/drivers/gpu/drm/nouveau/nvkm/core/subdev.c new file mode 100644 index 000000000..a74b7acb6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/subdev.c @@ -0,0 +1,194 @@ +/* + * 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 +#include +#include +#include + +const char * +nvkm_subdev_type[NVKM_SUBDEV_NR] = { +#define NVKM_LAYOUT_ONCE(type,data,ptr,...) [type] = #ptr, +#define NVKM_LAYOUT_INST(A...) NVKM_LAYOUT_ONCE(A) +#include +#undef NVKM_LAYOUT_ONCE +#undef NVKM_LAYOUT_INST +}; + +void +nvkm_subdev_intr(struct nvkm_subdev *subdev) +{ + if (subdev->func->intr) + subdev->func->intr(subdev); +} + +int +nvkm_subdev_info(struct nvkm_subdev *subdev, u64 mthd, u64 *data) +{ + if (subdev->func->info) + return subdev->func->info(subdev, mthd, data); + return -ENOSYS; +} + +int +nvkm_subdev_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_device *device = subdev->device; + const char *action = suspend ? "suspend" : "fini"; + s64 time; + + nvkm_trace(subdev, "%s running...\n", action); + time = ktime_to_us(ktime_get()); + + if (subdev->func->fini) { + int ret = subdev->func->fini(subdev, suspend); + if (ret) { + nvkm_error(subdev, "%s failed, %d\n", action, ret); + if (suspend) + return ret; + } + } + + nvkm_mc_reset(device, subdev->type, subdev->inst); + + time = ktime_to_us(ktime_get()) - time; + nvkm_trace(subdev, "%s completed in %lldus\n", action, time); + return 0; +} + +int +nvkm_subdev_preinit(struct nvkm_subdev *subdev) +{ + s64 time; + + nvkm_trace(subdev, "preinit running...\n"); + time = ktime_to_us(ktime_get()); + + if (subdev->func->preinit) { + int ret = subdev->func->preinit(subdev); + if (ret) { + nvkm_error(subdev, "preinit failed, %d\n", ret); + return ret; + } + } + + time = ktime_to_us(ktime_get()) - time; + nvkm_trace(subdev, "preinit completed in %lldus\n", time); + return 0; +} + +int +nvkm_subdev_init(struct nvkm_subdev *subdev) +{ + s64 time; + int ret; + + nvkm_trace(subdev, "init running...\n"); + time = ktime_to_us(ktime_get()); + + if (subdev->func->oneinit && !subdev->oneinit) { + s64 time; + nvkm_trace(subdev, "one-time init running...\n"); + time = ktime_to_us(ktime_get()); + ret = subdev->func->oneinit(subdev); + if (ret) { + nvkm_error(subdev, "one-time init failed, %d\n", ret); + return ret; + } + + subdev->oneinit = true; + time = ktime_to_us(ktime_get()) - time; + nvkm_trace(subdev, "one-time init completed in %lldus\n", time); + } + + if (subdev->func->init) { + ret = subdev->func->init(subdev); + if (ret) { + nvkm_error(subdev, "init failed, %d\n", ret); + return ret; + } + } + + time = ktime_to_us(ktime_get()) - time; + nvkm_trace(subdev, "init completed in %lldus\n", time); + return 0; +} + +void +nvkm_subdev_del(struct nvkm_subdev **psubdev) +{ + struct nvkm_subdev *subdev = *psubdev; + s64 time; + + if (subdev && !WARN_ON(!subdev->func)) { + nvkm_trace(subdev, "destroy running...\n"); + time = ktime_to_us(ktime_get()); + list_del(&subdev->head); + if (subdev->func->dtor) + *psubdev = subdev->func->dtor(subdev); + time = ktime_to_us(ktime_get()) - time; + nvkm_trace(subdev, "destroy completed in %lldus\n", time); + kfree(*psubdev); + *psubdev = NULL; + } +} + +void +nvkm_subdev_disable(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + struct nvkm_subdev *subdev; + list_for_each_entry(subdev, &device->subdev, head) { + if (subdev->type == type && subdev->inst == inst) { + *subdev->pself = NULL; + nvkm_subdev_del(&subdev); + break; + } + } +} + +void +nvkm_subdev_ctor(const struct nvkm_subdev_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_subdev *subdev) +{ + subdev->func = func; + subdev->device = device; + subdev->type = type; + subdev->inst = inst < 0 ? 0 : inst; + + if (inst >= 0) + snprintf(subdev->name, sizeof(subdev->name), "%s%d", nvkm_subdev_type[type], inst); + else + strscpy(subdev->name, nvkm_subdev_type[type], sizeof(subdev->name)); + subdev->debug = nvkm_dbgopt(device->dbgopt, subdev->name); + list_add_tail(&subdev->head, &device->subdev); +} + +int +nvkm_subdev_new_(const struct nvkm_subdev_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_subdev **psubdev) +{ + if (!(*psubdev = kzalloc(sizeof(**psubdev), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(func, device, type, inst, *psubdev); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild new file mode 100644 index 000000000..c6dfed18f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/Kbuild @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/falcon.o +nvkm-y += nvkm/engine/xtensa.o + +include $(src)/nvkm/engine/bsp/Kbuild +include $(src)/nvkm/engine/ce/Kbuild +include $(src)/nvkm/engine/cipher/Kbuild +include $(src)/nvkm/engine/device/Kbuild +include $(src)/nvkm/engine/disp/Kbuild +include $(src)/nvkm/engine/dma/Kbuild +include $(src)/nvkm/engine/fifo/Kbuild +include $(src)/nvkm/engine/gr/Kbuild +include $(src)/nvkm/engine/mpeg/Kbuild +include $(src)/nvkm/engine/msenc/Kbuild +include $(src)/nvkm/engine/mspdec/Kbuild +include $(src)/nvkm/engine/msppp/Kbuild +include $(src)/nvkm/engine/msvld/Kbuild +include $(src)/nvkm/engine/nvenc/Kbuild +include $(src)/nvkm/engine/nvdec/Kbuild +include $(src)/nvkm/engine/pm/Kbuild +include $(src)/nvkm/engine/sec/Kbuild +include $(src)/nvkm/engine/sec2/Kbuild +include $(src)/nvkm/engine/sw/Kbuild +include $(src)/nvkm/engine/vic/Kbuild +include $(src)/nvkm/engine/vp/Kbuild diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild new file mode 100644 index 000000000..b596b9905 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/bsp/Kbuild @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/bsp/g84.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c new file mode 100644 index 000000000..39f6db269 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c @@ -0,0 +1,44 @@ +/* + * 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, Ilia Mirkin + */ +#include + +#include + +static const struct nvkm_xtensa_func +g84_bsp = { + .fifo_val = 0x1111, + .unkd28 = 0x90044, + .sclass = { + { -1, -1, NV74_BSP }, + {} + } +}; + +int +g84_bsp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_xtensa_new_(&g84_bsp, device, type, inst, + device->chipset != 0x92, 0x103000, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild new file mode 100644 index 000000000..ba88613e1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/ce/gt215.o +nvkm-y += nvkm/engine/ce/gf100.o +nvkm-y += nvkm/engine/ce/gk104.o +nvkm-y += nvkm/engine/ce/gm107.o +nvkm-y += nvkm/engine/ce/gm200.o +nvkm-y += nvkm/engine/ce/gp100.o +nvkm-y += nvkm/engine/ce/gp102.o +nvkm-y += nvkm/engine/ce/gv100.o +nvkm-y += nvkm/engine/ce/tu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc new file mode 100644 index 000000000..6226bcd98 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/com.fuc @@ -0,0 +1,864 @@ +/* fuc microcode for copy engine on gt215- chipsets + * + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#ifdef GT215 +.section #gt215_ce_data +#else +.section #gf100_ce_data +#endif + +ctx_object: .b32 0 +#ifdef GT215 +ctx_dma: +ctx_dma_query: .b32 0 +ctx_dma_src: .b32 0 +ctx_dma_dst: .b32 0 +#endif +.equ #ctx_dma_count 3 +ctx_query_address_high: .b32 0 +ctx_query_address_low: .b32 0 +ctx_query_counter: .b32 0 +ctx_src_address_high: .b32 0 +ctx_src_address_low: .b32 0 +ctx_src_pitch: .b32 0 +ctx_src_tile_mode: .b32 0 +ctx_src_xsize: .b32 0 +ctx_src_ysize: .b32 0 +ctx_src_zsize: .b32 0 +ctx_src_zoff: .b32 0 +ctx_src_xoff: .b32 0 +ctx_src_yoff: .b32 0 +ctx_src_cpp: .b32 0 +ctx_dst_address_high: .b32 0 +ctx_dst_address_low: .b32 0 +ctx_dst_pitch: .b32 0 +ctx_dst_tile_mode: .b32 0 +ctx_dst_xsize: .b32 0 +ctx_dst_ysize: .b32 0 +ctx_dst_zsize: .b32 0 +ctx_dst_zoff: .b32 0 +ctx_dst_xoff: .b32 0 +ctx_dst_yoff: .b32 0 +ctx_dst_cpp: .b32 0 +ctx_format: .b32 0 +ctx_swz_const0: .b32 0 +ctx_swz_const1: .b32 0 +ctx_xcnt: .b32 0 +ctx_ycnt: .b32 0 +.align 256 + +dispatch_table: +// mthd 0x0000, NAME +.b16 0x000 1 +.b32 #ctx_object ~0xffffffff +// mthd 0x0100, NOP +.b16 0x040 1 +.b32 0x00010000 + #cmd_nop ~0xffffffff +// mthd 0x0140, PM_TRIGGER +.b16 0x050 1 +.b32 0x00010000 + #cmd_pm_trigger ~0xffffffff +#ifdef GT215 +// mthd 0x0180-0x018c, DMA_ +.b16 0x060 #ctx_dma_count +dispatch_dma: +.b32 0x00010000 + #cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff +.b32 0x00010000 + #cmd_dma ~0xffffffff +#endif +// mthd 0x0200-0x0218, SRC_TILE +.b16 0x80 7 +.b32 #ctx_src_tile_mode ~0x00000fff +.b32 #ctx_src_xsize ~0x0007ffff +.b32 #ctx_src_ysize ~0x00001fff +.b32 #ctx_src_zsize ~0x000007ff +.b32 #ctx_src_zoff ~0x00000fff +.b32 #ctx_src_xoff ~0x0007ffff +.b32 #ctx_src_yoff ~0x00001fff +// mthd 0x0220-0x0238, DST_TILE +.b16 0x88 7 +.b32 #ctx_dst_tile_mode ~0x00000fff +.b32 #ctx_dst_xsize ~0x0007ffff +.b32 #ctx_dst_ysize ~0x00001fff +.b32 #ctx_dst_zsize ~0x000007ff +.b32 #ctx_dst_zoff ~0x00000fff +.b32 #ctx_dst_xoff ~0x0007ffff +.b32 #ctx_dst_yoff ~0x00001fff +// mthd 0x0300-0x0304, EXEC, WRCACHE_FLUSH +.b16 0xc0 2 +.b32 0x00010000 + #cmd_exec ~0xffffffff +.b32 0x00010000 + #cmd_wrcache_flush ~0xffffffff +// mthd 0x030c-0x0340, various stuff +.b16 0xc3 14 +.b32 #ctx_src_address_high ~0x000000ff +.b32 #ctx_src_address_low ~0xffffffff +.b32 #ctx_dst_address_high ~0x000000ff +.b32 #ctx_dst_address_low ~0xffffffff +.b32 #ctx_src_pitch ~0x0007ffff +.b32 #ctx_dst_pitch ~0x0007ffff +.b32 #ctx_xcnt ~0x0000ffff +.b32 #ctx_ycnt ~0x00001fff +.b32 #ctx_format ~0x0333ffff +.b32 #ctx_swz_const0 ~0xffffffff +.b32 #ctx_swz_const1 ~0xffffffff +.b32 #ctx_query_address_high ~0x000000ff +.b32 #ctx_query_address_low ~0xffffffff +.b32 #ctx_query_counter ~0xffffffff +.b16 0x800 0 + +#ifdef GT215 +.section #gt215_ce_code +#else +.section #gf100_ce_code +#endif + +main: + clear b32 $r0 + mov $sp $r0 + + // setup i0 handler and route fifo and ctxswitch to it + mov $r1 #ih + mov $iv0 $r1 + mov $r1 0x400 + movw $r2 0xfff3 + sethi $r2 0 + iowr I[$r1 + 0x300] $r2 + + // enable interrupts + or $r2 0xc + iowr I[$r1] $r2 + bset $flags ie0 + + // enable fifo access and context switching + mov $r1 0x1200 + mov $r2 3 + iowr I[$r1] $r2 + + // sleep forever, waking for interrupts + bset $flags $p0 + spin: + sleep $p0 + bra #spin + +// i0 handler +ih: + iord $r1 I[$r0 + 0x200] + + and $r2 $r1 0x00000008 + bra e #ih_no_chsw + call #chsw + ih_no_chsw: + and $r2 $r1 0x00000004 + bra e #ih_no_cmd + call #dispatch + + ih_no_cmd: + and $r1 $r1 0x0000000c + iowr I[$r0 + 0x100] $r1 + iret + +// $p1 direction (0 = unload, 1 = load) +// $r3 channel +swctx: + mov $r4 0x7700 + mov $xtargets $r4 +#ifdef GT215 + // target 7 hardcoded to ctx dma object + mov $xdbase $r0 +#else + // read SCRATCH3 to decide if we are PCOPY0 or PCOPY1 + mov $r4 0x2100 + iord $r4 I[$r4 + 0] + and $r4 1 + shl b32 $r4 4 + add b32 $r4 0x30 + + // channel is in vram + mov $r15 0x61c + shl b32 $r15 6 + mov $r5 0x114 + iowrs I[$r15] $r5 + + // read 16-byte PCOPYn info, containing context pointer, from channel + shl b32 $r5 $r3 4 + add b32 $r5 2 + mov $xdbase $r5 + mov $r5 $sp + // get a chunk of stack space, aligned to 256 byte boundary + sub b32 $r5 0x100 + mov $r6 0xff + not b32 $r6 + and $r5 $r6 + sethi $r5 0x00020000 + xdld $r4 $r5 + xdwait + sethi $r5 0 + + // set context pointer, from within channel VM + mov $r14 0 + iowrs I[$r15] $r14 + ld b32 $r4 D[$r5 + 0] + shr b32 $r4 8 + ld b32 $r6 D[$r5 + 4] + shl b32 $r6 24 + or $r4 $r6 + mov $xdbase $r4 +#endif + // 256-byte context, at start of data segment + mov b32 $r4 $r0 + sethi $r4 0x60000 + + // swap! + bra $p1 #swctx_load + xdst $r0 $r4 + bra #swctx_done + swctx_load: + xdld $r0 $r4 + swctx_done: + xdwait + ret + +chsw: + // read current channel + mov $r2 0x1400 + iord $r3 I[$r2] + + // if it's active, unload it and return + xbit $r15 $r3 0x1e + bra e #chsw_no_unload + bclr $flags $p1 + call #swctx + bclr $r3 0x1e + iowr I[$r2] $r3 + mov $r4 1 + iowr I[$r2 + 0x200] $r4 + ret + + // read next channel + chsw_no_unload: + iord $r3 I[$r2 + 0x100] + + // is there a channel waiting to be loaded? + xbit $r13 $r3 0x1e + bra e #chsw_finish_load + bset $flags $p1 + call #swctx +#ifdef GT215 + // load dma objects back into TARGET regs + mov $r5 #ctx_dma + mov $r6 #ctx_dma_count + chsw_load_ctx_dma: + ld b32 $r7 D[$r5 + $r6 * 4] + add b32 $r8 $r6 0x180 + shl b32 $r8 8 + iowr I[$r8] $r7 + sub b32 $r6 1 + bra nc #chsw_load_ctx_dma +#endif + chsw_finish_load: + mov $r3 2 + iowr I[$r2 + 0x200] $r3 + ret + +dispatch: + // read incoming fifo command + mov $r3 0x1900 + iord $r2 I[$r3 + 0x100] + iord $r3 I[$r3 + 0x000] + and $r4 $r2 0x7ff + // $r2 will be used to store exception data + shl b32 $r2 0x10 + + // lookup method in the dispatch table, ILLEGAL_MTHD if not found + mov $r5 #dispatch_table + clear b32 $r6 + clear b32 $r7 + dispatch_loop: + ld b16 $r6 D[$r5 + 0] + ld b16 $r7 D[$r5 + 2] + add b32 $r5 4 + cmpu b32 $r4 $r6 + bra c #dispatch_illegal_mthd + add b32 $r7 $r6 + cmpu b32 $r4 $r7 + bra c #dispatch_valid_mthd + sub b32 $r7 $r6 + shl b32 $r7 3 + add b32 $r5 $r7 + bra #dispatch_loop + + // ensure no bits set in reserved fields, INVALID_BITFIELD + dispatch_valid_mthd: + sub b32 $r4 $r6 + shl b32 $r4 3 + add b32 $r4 $r5 + ld b32 $r5 D[$r4 + 4] + and $r5 $r3 + cmpu b32 $r5 0 + bra ne #dispatch_invalid_bitfield + + // depending on dispatch flags: execute method, or save data as state + ld b16 $r5 D[$r4 + 0] + ld b16 $r6 D[$r4 + 2] + cmpu b32 $r6 0 + bra ne #dispatch_cmd + st b32 D[$r5] $r3 + bra #dispatch_done + dispatch_cmd: + bclr $flags $p1 + call $r5 + bra $p1 #dispatch_error + bra #dispatch_done + + dispatch_invalid_bitfield: + or $r2 2 + dispatch_illegal_mthd: + or $r2 1 + + // store exception data in SCRATCH0/SCRATCH1, signal hostirq + dispatch_error: + mov $r4 0x1000 + iowr I[$r4 + 0x000] $r2 + iowr I[$r4 + 0x100] $r3 + mov $r2 0x40 + iowr I[$r0] $r2 + hostirq_wait: + iord $r2 I[$r0 + 0x200] + and $r2 0x40 + cmpu b32 $r2 0 + bra ne #hostirq_wait + + dispatch_done: + mov $r2 0x1d00 + mov $r3 1 + iowr I[$r2] $r3 + ret + +// No-operation +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_nop: + ret + +// PM_TRIGGER +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_pm_trigger: + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x20000 + iowr I[$r2] $r3 + ret + +#ifdef GT215 +// SET_DMA_* method handler +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_dma: + sub b32 $r4 #dispatch_dma + shr b32 $r4 1 + bset $r3 0x1e + st b32 D[$r4 + #ctx_dma] $r3 + add b32 $r4 0x600 + shl b32 $r4 6 + iowr I[$r4] $r3 + ret +#endif + +// Calculates the hw swizzle mask and adjusts the surface's xcnt to match +// +cmd_exec_set_format: + // zero out a chunk of the stack to store the swizzle into + add $sp -0x10 + st b32 D[$sp + 0x00] $r0 + st b32 D[$sp + 0x04] $r0 + st b32 D[$sp + 0x08] $r0 + st b32 D[$sp + 0x0c] $r0 + + // extract cpp, src_ncomp and dst_ncomp from FORMAT + ld b32 $r4 D[$r0 + #ctx_format] + extr $r5 $r4 16:17 + add b32 $r5 1 + extr $r6 $r4 20:21 + add b32 $r6 1 + extr $r7 $r4 24:25 + add b32 $r7 1 + + // convert FORMAT swizzle mask to hw swizzle mask + bclr $flags $p2 + clear b32 $r8 + clear b32 $r9 + ncomp_loop: + and $r10 $r4 0xf + shr b32 $r4 4 + clear b32 $r11 + bpc_loop: + cmpu b8 $r10 4 + bra nc #cmp_c0 + mulu $r12 $r10 $r5 + add b32 $r12 $r11 + bset $flags $p2 + bra #bpc_next + cmp_c0: + bra ne #cmp_c1 + mov $r12 0x10 + add b32 $r12 $r11 + bra #bpc_next + cmp_c1: + cmpu b8 $r10 6 + bra nc #cmp_zero + mov $r12 0x14 + add b32 $r12 $r11 + bra #bpc_next + cmp_zero: + mov $r12 0x80 + bpc_next: + st b8 D[$sp + $r8] $r12 + add b32 $r8 1 + add b32 $r11 1 + cmpu b32 $r11 $r5 + bra c #bpc_loop + add b32 $r9 1 + cmpu b32 $r9 $r7 + bra c #ncomp_loop + + // SRC_XCNT = (xcnt * src_cpp), or 0 if no src ref in swz (hw will hang) + mulu $r6 $r5 + st b32 D[$r0 + #ctx_src_cpp] $r6 + ld b32 $r8 D[$r0 + #ctx_xcnt] + mulu $r6 $r8 + bra $p2 #dst_xcnt + clear b32 $r6 + + dst_xcnt: + mulu $r7 $r5 + st b32 D[$r0 + #ctx_dst_cpp] $r7 + mulu $r7 $r8 + + mov $r5 0x810 + shl b32 $r5 6 + iowr I[$r5 + 0x000] $r6 + iowr I[$r5 + 0x100] $r7 + add b32 $r5 0x800 + ld b32 $r6 D[$r0 + #ctx_dst_cpp] + sub b32 $r6 1 + shl b32 $r6 8 + ld b32 $r7 D[$r0 + #ctx_src_cpp] + sub b32 $r7 1 + or $r6 $r7 + iowr I[$r5 + 0x000] $r6 + add b32 $r5 0x100 + ld b32 $r6 D[$sp + 0x00] + iowr I[$r5 + 0x000] $r6 + ld b32 $r6 D[$sp + 0x04] + iowr I[$r5 + 0x100] $r6 + ld b32 $r6 D[$sp + 0x08] + iowr I[$r5 + 0x200] $r6 + ld b32 $r6 D[$sp + 0x0c] + iowr I[$r5 + 0x300] $r6 + add b32 $r5 0x400 + ld b32 $r6 D[$r0 + #ctx_swz_const0] + iowr I[$r5 + 0x000] $r6 + ld b32 $r6 D[$r0 + #ctx_swz_const1] + iowr I[$r5 + 0x100] $r6 + add $sp 0x10 + ret + +// Setup to handle a tiled surface +// +// Calculates a number of parameters the hardware requires in order +// to correctly handle tiling. +// +// Offset calculation is performed as follows (Tp/Th/Td from TILE_MODE): +// nTx = round_up(w * cpp, 1 << Tp) >> Tp +// nTy = round_up(h, 1 << Th) >> Th +// Txo = (x * cpp) & ((1 << Tp) - 1) +// Tx = (x * cpp) >> Tp +// Tyo = y & ((1 << Th) - 1) +// Ty = y >> Th +// Tzo = z & ((1 << Td) - 1) +// Tz = z >> Td +// +// off = (Tzo << Tp << Th) + (Tyo << Tp) + Txo +// off += ((Tz * nTy * nTx)) + (Ty * nTx) + Tx) << Td << Th << Tp; +// +// Inputs: +// $r4: hw command (0x104800) +// $r5: ctx offset adjustment for src/dst selection +// $p2: set if dst surface +// +cmd_exec_set_surface_tiled: + // translate TILE_MODE into Tp, Th, Td shift values + ld b32 $r7 D[$r5 + #ctx_src_tile_mode] + extr $r9 $r7 8:11 + extr $r8 $r7 4:7 +#ifdef GT215 + add b32 $r8 2 +#else + add b32 $r8 3 +#endif + extr $r7 $r7 0:3 + cmp b32 $r7 0xe + bra ne #xtile64 + mov $r7 4 + bra #xtileok + xtile64: + xbit $r7 $flags $p2 + add b32 $r7 17 + bset $r4 $r7 + mov $r7 6 + xtileok: + + // Op = (x * cpp) & ((1 << Tp) - 1) + // Tx = (x * cpp) >> Tp + ld b32 $r10 D[$r5 + #ctx_src_xoff] + ld b32 $r11 D[$r5 + #ctx_src_cpp] + mulu $r10 $r11 + mov $r11 1 + shl b32 $r11 $r7 + sub b32 $r11 1 + and $r12 $r10 $r11 + shr b32 $r10 $r7 + + // Tyo = y & ((1 << Th) - 1) + // Ty = y >> Th + ld b32 $r13 D[$r5 + #ctx_src_yoff] + mov $r14 1 + shl b32 $r14 $r8 + sub b32 $r14 1 + and $r11 $r13 $r14 + shr b32 $r13 $r8 + + // YTILE = ((1 << Th) << 12) | ((1 << Th) - Tyo) + add b32 $r14 1 + shl b32 $r15 $r14 12 + sub b32 $r14 $r11 + or $r15 $r14 + xbit $r6 $flags $p2 + add b32 $r6 0x208 + shl b32 $r6 8 + iowr I[$r6 + 0x000] $r15 + + // Op += Tyo << Tp + shl b32 $r11 $r7 + add b32 $r12 $r11 + + // nTx = ((w * cpp) + ((1 << Tp) - 1) >> Tp) + ld b32 $r15 D[$r5 + #ctx_src_xsize] + ld b32 $r11 D[$r5 + #ctx_src_cpp] + mulu $r15 $r11 + mov $r11 1 + shl b32 $r11 $r7 + sub b32 $r11 1 + add b32 $r15 $r11 + shr b32 $r15 $r7 + push $r15 + + // nTy = (h + ((1 << Th) - 1)) >> Th + ld b32 $r15 D[$r5 + #ctx_src_ysize] + mov $r11 1 + shl b32 $r11 $r8 + sub b32 $r11 1 + add b32 $r15 $r11 + shr b32 $r15 $r8 + push $r15 + + // Tys = Tp + Th + // CFG_YZ_TILE_SIZE = ((1 << Th) >> 2) << Td + add b32 $r7 $r8 + sub b32 $r8 2 + mov $r11 1 + shl b32 $r11 $r8 + shl b32 $r11 $r9 + + // Tzo = z & ((1 << Td) - 1) + // Tz = z >> Td + // Op += Tzo << Tys + // Ts = Tys + Td + ld b32 $r8 D[$r5 + #ctx_src_zoff] + mov $r14 1 + shl b32 $r14 $r9 + sub b32 $r14 1 + and $r15 $r8 $r14 + shl b32 $r15 $r7 + add b32 $r12 $r15 + add b32 $r7 $r9 + shr b32 $r8 $r9 + + // Ot = ((Tz * nTy * nTx) + (Ty * nTx) + Tx) << Ts + pop $r15 + pop $r9 + mulu $r13 $r9 + add b32 $r10 $r13 + mulu $r8 $r9 + mulu $r8 $r15 + add b32 $r10 $r8 + shl b32 $r10 $r7 + + // PITCH = (nTx - 1) << Ts + sub b32 $r9 1 + shl b32 $r9 $r7 + iowr I[$r6 + 0x200] $r9 + + // SRC_ADDRESS_LOW = (Ot + Op) & 0xffffffff + // CFG_ADDRESS_HIGH |= ((Ot + Op) >> 32) << 16 + ld b32 $r7 D[$r5 + #ctx_src_address_low] + ld b32 $r8 D[$r5 + #ctx_src_address_high] + add b32 $r10 $r12 + add b32 $r7 $r10 + adc b32 $r8 0 + shl b32 $r8 16 + or $r8 $r11 + sub b32 $r6 0x600 + iowr I[$r6 + 0x000] $r7 + add b32 $r6 0x400 + iowr I[$r6 + 0x000] $r8 + ret + +// Setup to handle a linear surface +// +// Nothing to see here.. Sets ADDRESS and PITCH, pretty non-exciting +// +cmd_exec_set_surface_linear: + xbit $r6 $flags $p2 + add b32 $r6 0x202 + shl b32 $r6 8 + ld b32 $r7 D[$r5 + #ctx_src_address_low] + iowr I[$r6 + 0x000] $r7 + add b32 $r6 0x400 + ld b32 $r7 D[$r5 + #ctx_src_address_high] + shl b32 $r7 16 + iowr I[$r6 + 0x000] $r7 + add b32 $r6 0x400 + ld b32 $r7 D[$r5 + #ctx_src_pitch] + iowr I[$r6 + 0x000] $r7 + ret + +// wait for regs to be available for use +cmd_exec_wait: + push $r0 + push $r1 + mov $r0 0x800 + shl b32 $r0 6 + loop: + iord $r1 I[$r0] + and $r1 1 + bra ne #loop + pop $r1 + pop $r0 + ret + +cmd_exec_query: + // if QUERY_SHORT not set, write out { -, 0, TIME_LO, TIME_HI } + xbit $r4 $r3 13 + bra ne #query_counter + call #cmd_exec_wait + mov $r4 0x80c + shl b32 $r4 6 + ld b32 $r5 D[$r0 + #ctx_query_address_low] + add b32 $r5 4 + iowr I[$r4 + 0x000] $r5 + iowr I[$r4 + 0x100] $r0 + mov $r5 0xc + iowr I[$r4 + 0x200] $r5 + add b32 $r4 0x400 + ld b32 $r5 D[$r0 + #ctx_query_address_high] + shl b32 $r5 16 + iowr I[$r4 + 0x000] $r5 + add b32 $r4 0x500 + mov $r5 0x00000b00 + sethi $r5 0x00010000 + iowr I[$r4 + 0x000] $r5 + mov $r5 0x00004040 + shl b32 $r5 1 + sethi $r5 0x80800000 + iowr I[$r4 + 0x100] $r5 + mov $r5 0x00001110 + sethi $r5 0x13120000 + iowr I[$r4 + 0x200] $r5 + mov $r5 0x00001514 + sethi $r5 0x17160000 + iowr I[$r4 + 0x300] $r5 + mov $r5 0x00002601 + sethi $r5 0x00010000 + mov $r4 0x800 + shl b32 $r4 6 + iowr I[$r4 + 0x000] $r5 + + // write COUNTER + query_counter: + call #cmd_exec_wait + mov $r4 0x80c + shl b32 $r4 6 + ld b32 $r5 D[$r0 + #ctx_query_address_low] + iowr I[$r4 + 0x000] $r5 + iowr I[$r4 + 0x100] $r0 + mov $r5 0x4 + iowr I[$r4 + 0x200] $r5 + add b32 $r4 0x400 + ld b32 $r5 D[$r0 + #ctx_query_address_high] + shl b32 $r5 16 + iowr I[$r4 + 0x000] $r5 + add b32 $r4 0x500 + mov $r5 0x00000300 + iowr I[$r4 + 0x000] $r5 + mov $r5 0x00001110 + sethi $r5 0x13120000 + iowr I[$r4 + 0x100] $r5 + ld b32 $r5 D[$r0 + #ctx_query_counter] + add b32 $r4 0x500 + iowr I[$r4 + 0x000] $r5 + mov $r5 0x00002601 + sethi $r5 0x00010000 + mov $r4 0x800 + shl b32 $r4 6 + iowr I[$r4 + 0x000] $r5 + ret + +// Execute a copy operation +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// 000002000 QUERY_SHORT +// 000001000 QUERY +// 000000100 DST_LINEAR +// 000000010 SRC_LINEAR +// 000000001 FORMAT +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_exec: + call #cmd_exec_wait + + // if format requested, call function to calculate it, otherwise + // fill in cpp/xcnt for both surfaces as if (cpp == 1) + xbit $r15 $r3 0 + bra e #cmd_exec_no_format + call #cmd_exec_set_format + mov $r4 0x200 + bra #cmd_exec_init_src_surface + cmd_exec_no_format: + mov $r6 0x810 + shl b32 $r6 6 + mov $r7 1 + st b32 D[$r0 + #ctx_src_cpp] $r7 + st b32 D[$r0 + #ctx_dst_cpp] $r7 + ld b32 $r7 D[$r0 + #ctx_xcnt] + iowr I[$r6 + 0x000] $r7 + iowr I[$r6 + 0x100] $r7 + clear b32 $r4 + + cmd_exec_init_src_surface: + bclr $flags $p2 + clear b32 $r5 + xbit $r15 $r3 4 + bra e #src_tiled + call #cmd_exec_set_surface_linear + bra #cmd_exec_init_dst_surface + src_tiled: + call #cmd_exec_set_surface_tiled + bset $r4 7 + + cmd_exec_init_dst_surface: + bset $flags $p2 + mov $r5 #ctx_dst_address_high - #ctx_src_address_high + xbit $r15 $r3 8 + bra e #dst_tiled + call #cmd_exec_set_surface_linear + bra #cmd_exec_kick + dst_tiled: + call #cmd_exec_set_surface_tiled + bset $r4 8 + + cmd_exec_kick: + mov $r5 0x800 + shl b32 $r5 6 + ld b32 $r6 D[$r0 + #ctx_ycnt] + iowr I[$r5 + 0x100] $r6 + mov $r6 0x0041 + // SRC_TARGET = 1, DST_TARGET = 2 + sethi $r6 0x44000000 + or $r4 $r6 + iowr I[$r5] $r4 + + // if requested, queue up a QUERY write after the copy has completed + xbit $r15 $r3 12 + bra e #cmd_exec_done + call #cmd_exec_query + + cmd_exec_done: + ret + +// Flush write cache +// +// Inputs: +// $r1: irqh state +// $r2: hostirq state +// $r3: data +// $r4: dispatch table entry +// Outputs: +// $r1: irqh state +// $p1: set on error +// $r2: hostirq state +// $r3: data +cmd_wrcache_flush: + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x10000 + iowr I[$r2] $r3 + ret + +.align 0x100 diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 new file mode 100644 index 000000000..36f0a99ac --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3 @@ -0,0 +1,2 @@ +#define GF100 +#include "com.fuc" diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h new file mode 100644 index 000000000..96d934f81 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gf100.fuc3.h @@ -0,0 +1,607 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gf100_ce_data[] = { +/* 0x0000: ctx_object */ + 0x00000000, +/* 0x0004: ctx_query_address_high */ + 0x00000000, +/* 0x0008: ctx_query_address_low */ + 0x00000000, +/* 0x000c: ctx_query_counter */ + 0x00000000, +/* 0x0010: ctx_src_address_high */ + 0x00000000, +/* 0x0014: ctx_src_address_low */ + 0x00000000, +/* 0x0018: ctx_src_pitch */ + 0x00000000, +/* 0x001c: ctx_src_tile_mode */ + 0x00000000, +/* 0x0020: ctx_src_xsize */ + 0x00000000, +/* 0x0024: ctx_src_ysize */ + 0x00000000, +/* 0x0028: ctx_src_zsize */ + 0x00000000, +/* 0x002c: ctx_src_zoff */ + 0x00000000, +/* 0x0030: ctx_src_xoff */ + 0x00000000, +/* 0x0034: ctx_src_yoff */ + 0x00000000, +/* 0x0038: ctx_src_cpp */ + 0x00000000, +/* 0x003c: ctx_dst_address_high */ + 0x00000000, +/* 0x0040: ctx_dst_address_low */ + 0x00000000, +/* 0x0044: ctx_dst_pitch */ + 0x00000000, +/* 0x0048: ctx_dst_tile_mode */ + 0x00000000, +/* 0x004c: ctx_dst_xsize */ + 0x00000000, +/* 0x0050: ctx_dst_ysize */ + 0x00000000, +/* 0x0054: ctx_dst_zsize */ + 0x00000000, +/* 0x0058: ctx_dst_zoff */ + 0x00000000, +/* 0x005c: ctx_dst_xoff */ + 0x00000000, +/* 0x0060: ctx_dst_yoff */ + 0x00000000, +/* 0x0064: ctx_dst_cpp */ + 0x00000000, +/* 0x0068: ctx_format */ + 0x00000000, +/* 0x006c: ctx_swz_const0 */ + 0x00000000, +/* 0x0070: ctx_swz_const1 */ + 0x00000000, +/* 0x0074: ctx_xcnt */ + 0x00000000, +/* 0x0078: ctx_ycnt */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: dispatch_table */ + 0x00010000, + 0x00000000, + 0x00000000, + 0x00010040, + 0x0001019f, + 0x00000000, + 0x00010050, + 0x000101a1, + 0x00000000, + 0x00070080, + 0x0000001c, + 0xfffff000, + 0x00000020, + 0xfff80000, + 0x00000024, + 0xffffe000, + 0x00000028, + 0xfffff800, + 0x0000002c, + 0xfffff000, + 0x00000030, + 0xfff80000, + 0x00000034, + 0xffffe000, + 0x00070088, + 0x00000048, + 0xfffff000, + 0x0000004c, + 0xfff80000, + 0x00000050, + 0xffffe000, + 0x00000054, + 0xfffff800, + 0x00000058, + 0xfffff000, + 0x0000005c, + 0xfff80000, + 0x00000060, + 0xffffe000, + 0x000200c0, + 0x000104b8, + 0x00000000, + 0x00010541, + 0x00000000, + 0x000e00c3, + 0x00000010, + 0xffffff00, + 0x00000014, + 0x00000000, + 0x0000003c, + 0xffffff00, + 0x00000040, + 0x00000000, + 0x00000018, + 0xfff80000, + 0x00000044, + 0xfff80000, + 0x00000074, + 0xffff0000, + 0x00000078, + 0xffffe000, + 0x00000068, + 0xfccc0000, + 0x0000006c, + 0x00000000, + 0x00000070, + 0x00000000, + 0x00000004, + 0xffffff00, + 0x00000008, + 0x00000000, + 0x0000000c, + 0x00000000, + 0x00000800, +}; + +static uint32_t gf100_ce_code[] = { +/* 0x0000: main */ + 0x04fe04bd, + 0x3517f000, + 0xf10010fe, + 0xf1040017, + 0xf0fff327, + 0x12d00023, + 0x0c25f0c0, + 0xf40012d0, + 0x17f11031, + 0x27f01200, + 0x0012d003, +/* 0x002f: spin */ + 0xf40031f4, + 0x0ef40028, +/* 0x0035: ih */ + 0x8001cffd, + 0xf40812c4, + 0x21f4060b, +/* 0x0041: ih_no_chsw */ + 0x0412c4ca, + 0xf5070bf4, +/* 0x004b: ih_no_cmd */ + 0xc4010221, + 0x01d00c11, +/* 0x0053: swctx */ + 0xf101f840, + 0xfe770047, + 0x47f1004b, + 0x44cf2100, + 0x0144f000, + 0xb60444b6, + 0xf7f13040, + 0xf4b6061c, + 0x1457f106, + 0x00f5d101, + 0xb6043594, + 0x57fe0250, + 0x0145fe00, + 0x010052b7, + 0x00ff67f1, + 0x56fd60bd, + 0x0253f004, + 0xf80545fa, + 0x0053f003, + 0xd100e7f0, + 0x549800fe, + 0x0845b600, + 0xb6015698, + 0x46fd1864, + 0x0047fe05, + 0xf00204b9, + 0x01f40643, + 0x0604fa09, +/* 0x00c3: swctx_load */ + 0xfa060ef4, +/* 0x00c6: swctx_done */ + 0x03f80504, +/* 0x00ca: chsw */ + 0x27f100f8, + 0x23cf1400, + 0x1e3fc800, + 0xf4170bf4, + 0x21f40132, + 0x1e3af053, + 0xf00023d0, + 0x24d00147, +/* 0x00eb: chsw_no_unload */ + 0xcf00f880, + 0x3dc84023, + 0x090bf41e, + 0xf40131f4, +/* 0x00fa: chsw_finish_load */ + 0x37f05321, + 0x8023d002, +/* 0x0102: dispatch */ + 0x37f100f8, + 0x32cf1900, + 0x0033cf40, + 0x07ff24e4, + 0xf11024b6, + 0xbd010057, +/* 0x011b: dispatch_loop */ + 0x5874bd64, + 0x57580056, + 0x0450b601, + 0xf40446b8, + 0x76bb4d08, + 0x0447b800, + 0xbb0f08f4, + 0x74b60276, + 0x0057bb03, +/* 0x013f: dispatch_valid_mthd */ + 0xbbdf0ef4, + 0x44b60246, + 0x0045bb03, + 0xfd014598, + 0x54b00453, + 0x201bf400, + 0x58004558, + 0x64b00146, + 0x091bf400, + 0xf4005380, +/* 0x0166: dispatch_cmd */ + 0x32f4300e, + 0xf455f901, + 0x0ef40c01, +/* 0x0171: dispatch_invalid_bitfield */ + 0x0225f025, +/* 0x0174: dispatch_illegal_mthd */ +/* 0x0177: dispatch_error */ + 0xf10125f0, + 0xd0100047, + 0x43d00042, + 0x4027f040, +/* 0x0187: hostirq_wait */ + 0xcf0002d0, + 0x24f08002, + 0x0024b040, +/* 0x0193: dispatch_done */ + 0xf1f71bf4, + 0xf01d0027, + 0x23d00137, +/* 0x019f: cmd_nop */ + 0xf800f800, +/* 0x01a1: cmd_pm_trigger */ + 0x0027f100, + 0xf034bd22, + 0x23d00233, +/* 0x01af: cmd_exec_set_format */ + 0xf400f800, + 0x01b0f030, + 0x0101b000, + 0xb00201b0, + 0x04980301, + 0x3045c71a, + 0xc70150b6, + 0x60b63446, + 0x3847c701, + 0xf40170b6, + 0x84bd0232, +/* 0x01da: ncomp_loop */ + 0x4ac494bd, + 0x0445b60f, +/* 0x01e2: bpc_loop */ + 0xa430b4bd, + 0x0f18f404, + 0xbbc0a5ff, + 0x31f400cb, + 0x220ef402, +/* 0x01f4: cmp_c0 */ + 0xf00c1bf4, + 0xcbbb10c7, + 0x160ef400, +/* 0x0200: cmp_c1 */ + 0xf406a430, + 0xc7f00c18, + 0x00cbbb14, +/* 0x020f: cmp_zero */ + 0xf1070ef4, +/* 0x0213: bpc_next */ + 0x380080c7, + 0x80b601c8, + 0x01b0b601, + 0xf404b5b8, + 0x90b6c308, + 0x0497b801, + 0xfdb208f4, + 0x06800065, + 0x1d08980e, + 0xf40068fd, + 0x64bd0502, +/* 0x023c: dst_xcnt */ + 0x800075fd, + 0x78fd1907, + 0x1057f100, + 0x0654b608, + 0xd00056d0, + 0x50b74057, + 0x06980800, + 0x0162b619, + 0x980864b6, + 0x72b60e07, + 0x0567fd01, + 0xb70056d0, + 0xb4010050, + 0x56d00060, + 0x0160b400, + 0xb44056d0, + 0x56d00260, + 0x0360b480, + 0xb7c056d0, + 0x98040050, + 0x56d01b06, + 0x1c069800, + 0xf44056d0, + 0x00f81030, +/* 0x029c: cmd_exec_set_surface_tiled */ + 0xc7075798, + 0x78c76879, + 0x0380b664, + 0xb06077c7, + 0x1bf40e76, + 0x0477f009, +/* 0x02b7: xtile64 */ + 0xf00f0ef4, + 0x70b6027c, + 0x0947fd11, +/* 0x02c3: xtileok */ + 0x980677f0, + 0x5b980c5a, + 0x00abfd0e, + 0xbb01b7f0, + 0xb2b604b7, + 0xc4abff01, + 0x9805a7bb, + 0xe7f00d5d, + 0x04e8bb01, + 0xff01e2b6, + 0xd8bbb4de, + 0x01e0b605, + 0xbb0cef94, + 0xfefd02eb, + 0x026cf005, + 0x020860b7, + 0xd00864b6, + 0xb7bb006f, + 0x00cbbb04, + 0x98085f98, + 0xfbfd0e5b, + 0x01b7f000, + 0xb604b7bb, + 0xfbbb01b2, + 0x05f7bb00, + 0x5f98f0f9, + 0x01b7f009, + 0xb604b8bb, + 0xfbbb01b2, + 0x05f8bb00, + 0x78bbf0f9, + 0x0282b600, + 0xbb01b7f0, + 0xb9bb04b8, + 0x0b589804, + 0xbb01e7f0, + 0xe2b604e9, + 0xf48eff01, + 0xbb04f7bb, + 0x79bb00cf, + 0x0589bb00, + 0x90fcf0fc, + 0xbb00d9fd, + 0x89fd00ad, + 0x008ffd00, + 0xbb00a8bb, + 0x92b604a7, + 0x0497bb01, + 0x988069d0, + 0x58980557, + 0x00acbb04, + 0xb6007abb, + 0x84b60081, + 0x058bfd10, + 0x060062b7, + 0xb70067d0, + 0xd0040060, + 0x00f80068, +/* 0x03a8: cmd_exec_set_surface_linear */ + 0xb7026cf0, + 0xb6020260, + 0x57980864, + 0x0067d005, + 0x040060b7, + 0xb6045798, + 0x67d01074, + 0x0060b700, + 0x06579804, + 0xf80067d0, +/* 0x03d1: cmd_exec_wait */ + 0xf900f900, + 0x0007f110, + 0x0604b608, +/* 0x03dc: loop */ + 0xf00001cf, + 0x1bf40114, + 0xfc10fcfa, +/* 0x03eb: cmd_exec_query */ + 0xc800f800, + 0x1bf40d34, + 0xd121f570, + 0x0c47f103, + 0x0644b608, + 0xb6020598, + 0x45d00450, + 0x4040d000, + 0xd00c57f0, + 0x40b78045, + 0x05980400, + 0x1054b601, + 0xb70045d0, + 0xf1050040, + 0xf00b0057, + 0x45d00153, + 0x4057f100, + 0x0154b640, + 0x808053f1, + 0xf14045d0, + 0xf1111057, + 0xd0131253, + 0x57f18045, + 0x53f11514, + 0x45d01716, + 0x0157f1c0, + 0x0153f026, + 0x080047f1, + 0xd00644b6, +/* 0x045e: query_counter */ + 0x21f50045, + 0x47f103d1, + 0x44b6080c, + 0x02059806, + 0xd00045d0, + 0x57f04040, + 0x8045d004, + 0x040040b7, + 0xb6010598, + 0x45d01054, + 0x0040b700, + 0x0057f105, + 0x0045d003, + 0x111057f1, + 0x131253f1, + 0x984045d0, + 0x40b70305, + 0x45d00500, + 0x0157f100, + 0x0153f026, + 0x080047f1, + 0xd00644b6, + 0x00f80045, +/* 0x04b8: cmd_exec */ + 0x03d121f5, + 0xf4003fc8, + 0x21f50e0b, + 0x47f101af, + 0x0ef40200, +/* 0x04cd: cmd_exec_no_format */ + 0x1067f11e, + 0x0664b608, + 0x800177f0, + 0x07800e07, + 0x1d079819, + 0xd00067d0, + 0x44bd4067, +/* 0x04e8: cmd_exec_init_src_surface */ + 0xbd0232f4, + 0x043fc854, + 0xf50a0bf4, + 0xf403a821, +/* 0x04fa: src_tiled */ + 0x21f50a0e, + 0x49f0029c, +/* 0x0501: cmd_exec_init_dst_surface */ + 0x0231f407, + 0xc82c57f0, + 0x0bf4083f, + 0xa821f50a, + 0x0a0ef403, +/* 0x0514: dst_tiled */ + 0x029c21f5, +/* 0x051b: cmd_exec_kick */ + 0xf10849f0, + 0xb6080057, + 0x06980654, + 0x4056d01e, + 0xf14167f0, + 0xfd440063, + 0x54d00546, + 0x0c3fc800, + 0xf5070bf4, +/* 0x053f: cmd_exec_done */ + 0xf803eb21, +/* 0x0541: cmd_wrcache_flush */ + 0x0027f100, + 0xf034bd22, + 0x23d00133, + 0x0000f800, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 new file mode 100644 index 000000000..07bda93cf --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3 @@ -0,0 +1,2 @@ +#define GT215 +#include "com.fuc" diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h new file mode 100644 index 000000000..d3fbd4ab5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/fuc/gt215.fuc3.h @@ -0,0 +1,621 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gt215_ce_data[] = { +/* 0x0000: ctx_object */ + 0x00000000, +/* 0x0004: ctx_dma */ +/* 0x0004: ctx_dma_query */ + 0x00000000, +/* 0x0008: ctx_dma_src */ + 0x00000000, +/* 0x000c: ctx_dma_dst */ + 0x00000000, +/* 0x0010: ctx_query_address_high */ + 0x00000000, +/* 0x0014: ctx_query_address_low */ + 0x00000000, +/* 0x0018: ctx_query_counter */ + 0x00000000, +/* 0x001c: ctx_src_address_high */ + 0x00000000, +/* 0x0020: ctx_src_address_low */ + 0x00000000, +/* 0x0024: ctx_src_pitch */ + 0x00000000, +/* 0x0028: ctx_src_tile_mode */ + 0x00000000, +/* 0x002c: ctx_src_xsize */ + 0x00000000, +/* 0x0030: ctx_src_ysize */ + 0x00000000, +/* 0x0034: ctx_src_zsize */ + 0x00000000, +/* 0x0038: ctx_src_zoff */ + 0x00000000, +/* 0x003c: ctx_src_xoff */ + 0x00000000, +/* 0x0040: ctx_src_yoff */ + 0x00000000, +/* 0x0044: ctx_src_cpp */ + 0x00000000, +/* 0x0048: ctx_dst_address_high */ + 0x00000000, +/* 0x004c: ctx_dst_address_low */ + 0x00000000, +/* 0x0050: ctx_dst_pitch */ + 0x00000000, +/* 0x0054: ctx_dst_tile_mode */ + 0x00000000, +/* 0x0058: ctx_dst_xsize */ + 0x00000000, +/* 0x005c: ctx_dst_ysize */ + 0x00000000, +/* 0x0060: ctx_dst_zsize */ + 0x00000000, +/* 0x0064: ctx_dst_zoff */ + 0x00000000, +/* 0x0068: ctx_dst_xoff */ + 0x00000000, +/* 0x006c: ctx_dst_yoff */ + 0x00000000, +/* 0x0070: ctx_dst_cpp */ + 0x00000000, +/* 0x0074: ctx_format */ + 0x00000000, +/* 0x0078: ctx_swz_const0 */ + 0x00000000, +/* 0x007c: ctx_swz_const1 */ + 0x00000000, +/* 0x0080: ctx_xcnt */ + 0x00000000, +/* 0x0084: ctx_ycnt */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: dispatch_table */ + 0x00010000, + 0x00000000, + 0x00000000, + 0x00010040, + 0x00010160, + 0x00000000, + 0x00010050, + 0x00010162, + 0x00000000, + 0x00030060, +/* 0x0128: dispatch_dma */ + 0x00010170, + 0x00000000, + 0x00010170, + 0x00000000, + 0x00010170, + 0x00000000, + 0x00070080, + 0x00000028, + 0xfffff000, + 0x0000002c, + 0xfff80000, + 0x00000030, + 0xffffe000, + 0x00000034, + 0xfffff800, + 0x00000038, + 0xfffff000, + 0x0000003c, + 0xfff80000, + 0x00000040, + 0xffffe000, + 0x00070088, + 0x00000054, + 0xfffff000, + 0x00000058, + 0xfff80000, + 0x0000005c, + 0xffffe000, + 0x00000060, + 0xfffff800, + 0x00000064, + 0xfffff000, + 0x00000068, + 0xfff80000, + 0x0000006c, + 0xffffe000, + 0x000200c0, + 0x00010492, + 0x00000000, + 0x0001051b, + 0x00000000, + 0x000e00c3, + 0x0000001c, + 0xffffff00, + 0x00000020, + 0x00000000, + 0x00000048, + 0xffffff00, + 0x0000004c, + 0x00000000, + 0x00000024, + 0xfff80000, + 0x00000050, + 0xfff80000, + 0x00000080, + 0xffff0000, + 0x00000084, + 0xffffe000, + 0x00000074, + 0xfccc0000, + 0x00000078, + 0x00000000, + 0x0000007c, + 0x00000000, + 0x00000010, + 0xffffff00, + 0x00000014, + 0x00000000, + 0x00000018, + 0x00000000, + 0x00000800, +}; + +static uint32_t gt215_ce_code[] = { +/* 0x0000: main */ + 0x04fe04bd, + 0x3517f000, + 0xf10010fe, + 0xf1040017, + 0xf0fff327, + 0x12d00023, + 0x0c25f0c0, + 0xf40012d0, + 0x17f11031, + 0x27f01200, + 0x0012d003, +/* 0x002f: spin */ + 0xf40031f4, + 0x0ef40028, +/* 0x0035: ih */ + 0x8001cffd, + 0xf40812c4, + 0x21f4060b, +/* 0x0041: ih_no_chsw */ + 0x0412c472, + 0xf4060bf4, +/* 0x004a: ih_no_cmd */ + 0x11c4c321, + 0x4001d00c, +/* 0x0052: swctx */ + 0x47f101f8, + 0x4bfe7700, + 0x0007fe00, + 0xf00204b9, + 0x01f40643, + 0x0604fa09, +/* 0x006b: swctx_load */ + 0xfa060ef4, +/* 0x006e: swctx_done */ + 0x03f80504, +/* 0x0072: chsw */ + 0x27f100f8, + 0x23cf1400, + 0x1e3fc800, + 0xf4170bf4, + 0x21f40132, + 0x1e3af052, + 0xf00023d0, + 0x24d00147, +/* 0x0093: chsw_no_unload */ + 0xcf00f880, + 0x3dc84023, + 0x220bf41e, + 0xf40131f4, + 0x57f05221, + 0x0367f004, +/* 0x00a8: chsw_load_ctx_dma */ + 0xa07856bc, + 0xb6018068, + 0x87d00884, + 0x0162b600, +/* 0x00bb: chsw_finish_load */ + 0xf0f018f4, + 0x23d00237, +/* 0x00c3: dispatch */ + 0xf100f880, + 0xcf190037, + 0x33cf4032, + 0xff24e400, + 0x1024b607, + 0x010057f1, + 0x74bd64bd, +/* 0x00dc: dispatch_loop */ + 0x58005658, + 0x50b60157, + 0x0446b804, + 0xbb4d08f4, + 0x47b80076, + 0x0f08f404, + 0xb60276bb, + 0x57bb0374, + 0xdf0ef400, +/* 0x0100: dispatch_valid_mthd */ + 0xb60246bb, + 0x45bb0344, + 0x01459800, + 0xb00453fd, + 0x1bf40054, + 0x00455820, + 0xb0014658, + 0x1bf40064, + 0x00538009, +/* 0x0127: dispatch_cmd */ + 0xf4300ef4, + 0x55f90132, + 0xf40c01f4, +/* 0x0132: dispatch_invalid_bitfield */ + 0x25f0250e, +/* 0x0135: dispatch_illegal_mthd */ + 0x0125f002, +/* 0x0138: dispatch_error */ + 0x100047f1, + 0xd00042d0, + 0x27f04043, + 0x0002d040, +/* 0x0148: hostirq_wait */ + 0xf08002cf, + 0x24b04024, + 0xf71bf400, +/* 0x0154: dispatch_done */ + 0x1d0027f1, + 0xd00137f0, + 0x00f80023, +/* 0x0160: cmd_nop */ +/* 0x0162: cmd_pm_trigger */ + 0x27f100f8, + 0x34bd2200, + 0xd00233f0, + 0x00f80023, +/* 0x0170: cmd_dma */ + 0x012842b7, + 0xf00145b6, + 0x43801e39, + 0x0040b701, + 0x0644b606, + 0xf80043d0, +/* 0x0189: cmd_exec_set_format */ + 0xf030f400, + 0xb00001b0, + 0x01b00101, + 0x0301b002, + 0xc71d0498, + 0x50b63045, + 0x3446c701, + 0xc70160b6, + 0x70b63847, + 0x0232f401, + 0x94bd84bd, +/* 0x01b4: ncomp_loop */ + 0xb60f4ac4, + 0xb4bd0445, +/* 0x01bc: bpc_loop */ + 0xf404a430, + 0xa5ff0f18, + 0x00cbbbc0, + 0xf40231f4, +/* 0x01ce: cmp_c0 */ + 0x1bf4220e, + 0x10c7f00c, + 0xf400cbbb, +/* 0x01da: cmp_c1 */ + 0xa430160e, + 0x0c18f406, + 0xbb14c7f0, + 0x0ef400cb, +/* 0x01e9: cmp_zero */ + 0x80c7f107, +/* 0x01ed: bpc_next */ + 0x01c83800, + 0xb60180b6, + 0xb5b801b0, + 0xc308f404, + 0xb80190b6, + 0x08f40497, + 0x0065fdb2, + 0x98110680, + 0x68fd2008, + 0x0502f400, +/* 0x0216: dst_xcnt */ + 0x75fd64bd, + 0x1c078000, + 0xf10078fd, + 0xb6081057, + 0x56d00654, + 0x4057d000, + 0x080050b7, + 0xb61c0698, + 0x64b60162, + 0x11079808, + 0xfd0172b6, + 0x56d00567, + 0x0050b700, + 0x0060b401, + 0xb40056d0, + 0x56d00160, + 0x0260b440, + 0xb48056d0, + 0x56d00360, + 0x0050b7c0, + 0x1e069804, + 0x980056d0, + 0x56d01f06, + 0x1030f440, +/* 0x0276: cmd_exec_set_surface_tiled */ + 0x579800f8, + 0x6879c70a, + 0xb66478c7, + 0x77c70280, + 0x0e76b060, + 0xf0091bf4, + 0x0ef40477, +/* 0x0291: xtile64 */ + 0x027cf00f, + 0xfd1170b6, + 0x77f00947, +/* 0x029d: xtileok */ + 0x0f5a9806, + 0xfd115b98, + 0xb7f000ab, + 0x04b7bb01, + 0xff01b2b6, + 0xa7bbc4ab, + 0x105d9805, + 0xbb01e7f0, + 0xe2b604e8, + 0xb4deff01, + 0xb605d8bb, + 0xef9401e0, + 0x02ebbb0c, + 0xf005fefd, + 0x60b7026c, + 0x64b60208, + 0x006fd008, + 0xbb04b7bb, + 0x5f9800cb, + 0x115b980b, + 0xf000fbfd, + 0xb7bb01b7, + 0x01b2b604, + 0xbb00fbbb, + 0xf0f905f7, + 0xf00c5f98, + 0xb8bb01b7, + 0x01b2b604, + 0xbb00fbbb, + 0xf0f905f8, + 0xb60078bb, + 0xb7f00282, + 0x04b8bb01, + 0x9804b9bb, + 0xe7f00e58, + 0x04e9bb01, + 0xff01e2b6, + 0xf7bbf48e, + 0x00cfbb04, + 0xbb0079bb, + 0xf0fc0589, + 0xd9fd90fc, + 0x00adbb00, + 0xfd0089fd, + 0xa8bb008f, + 0x04a7bb00, + 0xbb0192b6, + 0x69d00497, + 0x08579880, + 0xbb075898, + 0x7abb00ac, + 0x0081b600, + 0xfd1084b6, + 0x62b7058b, + 0x67d00600, + 0x0060b700, + 0x0068d004, +/* 0x0382: cmd_exec_set_surface_linear */ + 0x6cf000f8, + 0x0260b702, + 0x0864b602, + 0xd0085798, + 0x60b70067, + 0x57980400, + 0x1074b607, + 0xb70067d0, + 0x98040060, + 0x67d00957, +/* 0x03ab: cmd_exec_wait */ + 0xf900f800, + 0xf110f900, + 0xb6080007, +/* 0x03b6: loop */ + 0x01cf0604, + 0x0114f000, + 0xfcfa1bf4, + 0xf800fc10, +/* 0x03c5: cmd_exec_query */ + 0x0d34c800, + 0xf5701bf4, + 0xf103ab21, + 0xb6080c47, + 0x05980644, + 0x0450b605, + 0xd00045d0, + 0x57f04040, + 0x8045d00c, + 0x040040b7, + 0xb6040598, + 0x45d01054, + 0x0040b700, + 0x0057f105, + 0x0153f00b, + 0xf10045d0, + 0xb6404057, + 0x53f10154, + 0x45d08080, + 0x1057f140, + 0x1253f111, + 0x8045d013, + 0x151457f1, + 0x171653f1, + 0xf1c045d0, + 0xf0260157, + 0x47f10153, + 0x44b60800, + 0x0045d006, +/* 0x0438: query_counter */ + 0x03ab21f5, + 0x080c47f1, + 0x980644b6, + 0x45d00505, + 0x4040d000, + 0xd00457f0, + 0x40b78045, + 0x05980400, + 0x1054b604, + 0xb70045d0, + 0xf1050040, + 0xd0030057, + 0x57f10045, + 0x53f11110, + 0x45d01312, + 0x06059840, + 0x050040b7, + 0xf10045d0, + 0xf0260157, + 0x47f10153, + 0x44b60800, + 0x0045d006, +/* 0x0492: cmd_exec */ + 0x21f500f8, + 0x3fc803ab, + 0x0e0bf400, + 0x018921f5, + 0x020047f1, +/* 0x04a7: cmd_exec_no_format */ + 0xf11e0ef4, + 0xb6081067, + 0x77f00664, + 0x11078001, + 0x981c0780, + 0x67d02007, + 0x4067d000, +/* 0x04c2: cmd_exec_init_src_surface */ + 0x32f444bd, + 0xc854bd02, + 0x0bf4043f, + 0x8221f50a, + 0x0a0ef403, +/* 0x04d4: src_tiled */ + 0x027621f5, +/* 0x04db: cmd_exec_init_dst_surface */ + 0xf40749f0, + 0x57f00231, + 0x083fc82c, + 0xf50a0bf4, + 0xf4038221, +/* 0x04ee: dst_tiled */ + 0x21f50a0e, + 0x49f00276, +/* 0x04f5: cmd_exec_kick */ + 0x0057f108, + 0x0654b608, + 0xd0210698, + 0x67f04056, + 0x0063f141, + 0x0546fd44, + 0xc80054d0, + 0x0bf40c3f, + 0xc521f507, +/* 0x0519: cmd_exec_done */ +/* 0x051b: cmd_wrcache_flush */ + 0xf100f803, + 0xbd220027, + 0x0133f034, + 0xf80023d0, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c new file mode 100644 index 000000000..b9cc39565 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gf100.c @@ -0,0 +1,69 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "fuc/gf100.fuc3.h" + +#include + +static void +gf100_ce_init(struct nvkm_falcon *ce) +{ + nvkm_wr32(ce->engine.subdev.device, ce->addr + 0x084, ce->engine.subdev.inst); +} + +static const struct nvkm_falcon_func +gf100_ce0 = { + .code.data = gf100_ce_code, + .code.size = sizeof(gf100_ce_code), + .data.data = gf100_ce_data, + .data.size = sizeof(gf100_ce_data), + .init = gf100_ce_init, + .intr = gt215_ce_intr, + .sclass = { + { -1, -1, FERMI_DMA }, + {} + } +}; + +static const struct nvkm_falcon_func +gf100_ce1 = { + .code.data = gf100_ce_code, + .code.size = sizeof(gf100_ce_code), + .data.data = gf100_ce_data, + .data.size = sizeof(gf100_ce_data), + .init = gf100_ce_init, + .intr = gt215_ce_intr, + .sclass = { + { -1, -1, FERMI_DECOMPRESS }, + {} + } +}; + +int +gf100_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_falcon_new_(inst ? &gf100_ce1 : &gf100_ce0, device, type, inst, true, + 0x104000 + (inst * 0x1000), pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c new file mode 100644 index 000000000..27f29eb04 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gk104.c @@ -0,0 +1,101 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include + +#include + +static const struct nvkm_enum +gk104_ce_launcherr_report[] = { + { 0x0, "NO_ERR" }, + { 0x1, "2D_LAYER_EXCEEDS_DEPTH" }, + { 0x2, "INVALID_ARGUMENT" }, + { 0x3, "MEM2MEM_RECT_OUT_OF_BOUNDS" }, + { 0x4, "SRC_LINE_EXCEEDS_PITCH" }, + { 0x5, "SRC_LINE_EXCEEDS_NEG_PITCH" }, + { 0x6, "DST_LINE_EXCEEDS_PITCH" }, + { 0x7, "DST_LINE_EXCEEDS_NEG_PITCH" }, + { 0x8, "BAD_SRC_PIXEL_COMP_REF" }, + { 0x9, "INVALID_VALUE" }, + { 0xa, "UNUSED_FIELD" }, + { 0xb, "INVALID_OPERATION" }, + {} +}; + +static void +gk104_ce_intr_launcherr(struct nvkm_engine *ce, const u32 base) +{ + struct nvkm_subdev *subdev = &ce->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x104f14 + base); + const struct nvkm_enum *en = + nvkm_enum_find(gk104_ce_launcherr_report, stat & 0x0000000f); + nvkm_warn(subdev, "LAUNCHERR %08x [%s]\n", stat, en ? en->name : ""); + nvkm_wr32(device, 0x104f14 + base, 0x00000000); +} + +void +gk104_ce_intr(struct nvkm_engine *ce) +{ + struct nvkm_subdev *subdev = &ce->subdev; + struct nvkm_device *device = subdev->device; + const u32 base = subdev->inst * 0x1000; + u32 mask = nvkm_rd32(device, 0x104904 + base); + u32 intr = nvkm_rd32(device, 0x104908 + base) & mask; + if (intr & 0x00000001) { + nvkm_warn(subdev, "BLOCKPIPE\n"); + nvkm_wr32(device, 0x104908 + base, 0x00000001); + intr &= ~0x00000001; + } + if (intr & 0x00000002) { + nvkm_warn(subdev, "NONBLOCKPIPE\n"); + nvkm_wr32(device, 0x104908 + base, 0x00000002); + intr &= ~0x00000002; + } + if (intr & 0x00000004) { + gk104_ce_intr_launcherr(ce, base); + nvkm_wr32(device, 0x104908 + base, 0x00000004); + intr &= ~0x00000004; + } + if (intr) { + nvkm_warn(subdev, "intr %08x\n", intr); + nvkm_wr32(device, 0x104908 + base, intr); + } +} + +static const struct nvkm_engine_func +gk104_ce = { + .intr = gk104_ce_intr, + .sclass = { + { -1, -1, KEPLER_DMA_COPY_A }, + {} + } +}; + +int +gk104_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&gk104_ce, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm107.c new file mode 100644 index 000000000..c3c476592 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm107.c @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +static const struct nvkm_engine_func +gm107_ce = { + .intr = gk104_ce_intr, + .sclass = { + { -1, -1, KEPLER_DMA_COPY_A }, + { -1, -1, MAXWELL_DMA_COPY_A }, + {} + } +}; + +int +gm107_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&gm107_ce, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm200.c new file mode 100644 index 000000000..d2db61865 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gm200.c @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +static const struct nvkm_engine_func +gm200_ce = { + .intr = gk104_ce_intr, + .sclass = { + { -1, -1, MAXWELL_DMA_COPY_A }, + {} + } +}; + +int +gm200_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&gm200_ce, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gp100.c new file mode 100644 index 000000000..a4f08a447 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gp100.c @@ -0,0 +1,102 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include + +#include + +static const struct nvkm_enum +gp100_ce_launcherr_report[] = { + { 0x0, "NO_ERR" }, + { 0x1, "2D_LAYER_EXCEEDS_DEPTH" }, + { 0x2, "INVALID_ALIGNMENT" }, + { 0x3, "MEM2MEM_RECT_OUT_OF_BOUNDS" }, + { 0x4, "SRC_LINE_EXCEEDS_PITCH" }, + { 0x5, "SRC_LINE_EXCEEDS_NEG_PITCH" }, + { 0x6, "DST_LINE_EXCEEDS_PITCH" }, + { 0x7, "DST_LINE_EXCEEDS_NEG_PITCH" }, + { 0x8, "BAD_SRC_PIXEL_COMP_REF" }, + { 0x9, "INVALID_VALUE" }, + { 0xa, "UNUSED_FIELD" }, + { 0xb, "INVALID_OPERATION" }, + { 0xc, "NO_RESOURCES" }, + { 0xd, "INVALID_CONFIG" }, + {} +}; + +static void +gp100_ce_intr_launcherr(struct nvkm_engine *ce, const u32 base) +{ + struct nvkm_subdev *subdev = &ce->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x104418 + base); + const struct nvkm_enum *en = + nvkm_enum_find(gp100_ce_launcherr_report, stat & 0x0000000f); + nvkm_warn(subdev, "LAUNCHERR %08x [%s]\n", stat, en ? en->name : ""); +} + +void +gp100_ce_intr(struct nvkm_engine *ce) +{ + struct nvkm_subdev *subdev = &ce->subdev; + struct nvkm_device *device = subdev->device; + const u32 base = subdev->inst * 0x80; + u32 mask = nvkm_rd32(device, 0x10440c + base); + u32 intr = nvkm_rd32(device, 0x104410 + base) & mask; + if (intr & 0x00000001) { //XXX: guess + nvkm_warn(subdev, "BLOCKPIPE\n"); + nvkm_wr32(device, 0x104410 + base, 0x00000001); + intr &= ~0x00000001; + } + if (intr & 0x00000002) { //XXX: guess + nvkm_warn(subdev, "NONBLOCKPIPE\n"); + nvkm_wr32(device, 0x104410 + base, 0x00000002); + intr &= ~0x00000002; + } + if (intr & 0x00000004) { + gp100_ce_intr_launcherr(ce, base); + nvkm_wr32(device, 0x104410 + base, 0x00000004); + intr &= ~0x00000004; + } + if (intr) { + nvkm_warn(subdev, "intr %08x\n", intr); + nvkm_wr32(device, 0x104410 + base, intr); + } +} + +static const struct nvkm_engine_func +gp100_ce = { + .intr = gp100_ce_intr, + .sclass = { + { -1, -1, PASCAL_DMA_COPY_A }, + {} + } +}; + +int +gp100_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&gp100_ce, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gp102.c new file mode 100644 index 000000000..180d497a9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gp102.c @@ -0,0 +1,44 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include + +#include + +static const struct nvkm_engine_func +gp102_ce = { + .intr = gp100_ce_intr, + .sclass = { + { -1, -1, PASCAL_DMA_COPY_B }, + { -1, -1, PASCAL_DMA_COPY_A }, + {} + } +}; + +int +gp102_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&gp102_ce, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c new file mode 100644 index 000000000..09a112af2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c @@ -0,0 +1,83 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "fuc/gt215.fuc3.h" + +#include +#include +#include +#include + +#include + +static const struct nvkm_enum +gt215_ce_isr_error_name[] = { + { 0x0001, "ILLEGAL_MTHD" }, + { 0x0002, "INVALID_ENUM" }, + { 0x0003, "INVALID_BITFIELD" }, + {} +}; + +void +gt215_ce_intr(struct nvkm_falcon *ce, struct nvkm_fifo_chan *chan) +{ + struct nvkm_subdev *subdev = &ce->engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 base = subdev->inst * 0x1000; + u32 ssta = nvkm_rd32(device, 0x104040 + base) & 0x0000ffff; + u32 addr = nvkm_rd32(device, 0x104040 + base) >> 16; + u32 mthd = (addr & 0x07ff) << 2; + u32 subc = (addr & 0x3800) >> 11; + u32 data = nvkm_rd32(device, 0x104044 + base); + const struct nvkm_enum *en = + nvkm_enum_find(gt215_ce_isr_error_name, ssta); + + nvkm_error(subdev, "DISPATCH_ERROR %04x [%s] ch %d [%010llx %s] " + "subc %d mthd %04x data %08x\n", ssta, + en ? en->name : "", chan ? chan->chid : -1, + chan ? chan->inst->addr : 0, + chan ? chan->object.client->name : "unknown", + subc, mthd, data); +} + +static const struct nvkm_falcon_func +gt215_ce = { + .code.data = gt215_ce_code, + .code.size = sizeof(gt215_ce_code), + .data.data = gt215_ce_data, + .data.size = sizeof(gt215_ce_data), + .intr = gt215_ce_intr, + .sclass = { + { -1, -1, GT212_DMA }, + {} + } +}; + +int +gt215_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_falcon_new_(>215_ce, device, type, -1, + (device->chipset != 0xaf), 0x104000, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gv100.c new file mode 100644 index 000000000..44021d139 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/gv100.c @@ -0,0 +1,64 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include + +#include + +static int +gv100_ce_cclass_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, int align, + struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_device *device = object->engine->subdev.device; + u32 size; + + /* Allocate fault method buffer (magics come from nvgpu). */ + size = nvkm_rd32(device, 0x104028); /* NV_PCE_PCE_MAP */ + size = 27 * 5 * (((9 + 1 + 3) * hweight32(size)) + 2); + size = roundup(size, PAGE_SIZE); + + return nvkm_gpuobj_new(device, size, align, true, parent, pgpuobj); +} + +const struct nvkm_object_func +gv100_ce_cclass = { + .bind = gv100_ce_cclass_bind, +}; + +static const struct nvkm_engine_func +gv100_ce = { + .intr = gp100_ce_intr, + .cclass = &gv100_ce_cclass, + .sclass = { + { -1, -1, VOLTA_DMA_COPY_A }, + {} + } +}; + +int +gv100_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&gv100_ce, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h new file mode 100644 index 000000000..cd53b9366 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_CE_PRIV_H__ +#define __NVKM_CE_PRIV_H__ +#include + +void gt215_ce_intr(struct nvkm_falcon *, struct nvkm_fifo_chan *); +void gk104_ce_intr(struct nvkm_engine *); +void gp100_ce_intr(struct nvkm_engine *); + +extern const struct nvkm_object_func gv100_ce_cclass; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/tu102.c new file mode 100644 index 000000000..9563c0175 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/tu102.c @@ -0,0 +1,41 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include + +static const struct nvkm_engine_func +tu102_ce = { + .intr = gp100_ce_intr, + .cclass = &gv100_ce_cclass, + .sclass = { + { -1, -1, TURING_DMA_COPY_A }, + {} + } +}; + +int +tu102_ce_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&tu102_ce, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild new file mode 100644 index 000000000..ffad341f4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/cipher/Kbuild @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/cipher/g84.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c new file mode 100644 index 000000000..be2a7181d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c @@ -0,0 +1,134 @@ +/* + * 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 +#include + +#include +#include +#include + +#include + +static int +g84_cipher_oclass_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + int ret = nvkm_gpuobj_new(object->engine->subdev.device, 16, + align, false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, object->oclass); + nvkm_wo32(*pgpuobj, 0x04, 0x00000000); + nvkm_wo32(*pgpuobj, 0x08, 0x00000000); + nvkm_wo32(*pgpuobj, 0x0c, 0x00000000); + nvkm_done(*pgpuobj); + } + return ret; +} + +static const struct nvkm_object_func +g84_cipher_oclass_func = { + .bind = g84_cipher_oclass_bind, +}; + +static int +g84_cipher_cclass_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + return nvkm_gpuobj_new(object->engine->subdev.device, 256, + align, true, parent, pgpuobj); + +} + +static const struct nvkm_object_func +g84_cipher_cclass = { + .bind = g84_cipher_cclass_bind, +}; + +static const struct nvkm_bitfield +g84_cipher_intr_mask[] = { + { 0x00000001, "INVALID_STATE" }, + { 0x00000002, "ILLEGAL_MTHD" }, + { 0x00000004, "ILLEGAL_CLASS" }, + { 0x00000080, "QUERY" }, + { 0x00000100, "FAULT" }, + {} +}; + +static void +g84_cipher_intr(struct nvkm_engine *cipher) +{ + struct nvkm_subdev *subdev = &cipher->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fifo *fifo = device->fifo; + struct nvkm_fifo_chan *chan; + u32 stat = nvkm_rd32(device, 0x102130); + u32 mthd = nvkm_rd32(device, 0x102190); + u32 data = nvkm_rd32(device, 0x102194); + u32 inst = nvkm_rd32(device, 0x102188) & 0x7fffffff; + unsigned long flags; + char msg[128]; + + chan = nvkm_fifo_chan_inst(fifo, (u64)inst << 12, &flags); + if (stat) { + nvkm_snprintbf(msg, sizeof(msg), g84_cipher_intr_mask, stat); + nvkm_error(subdev, "%08x [%s] ch %d [%010llx %s] " + "mthd %04x data %08x\n", stat, msg, + chan ? chan->chid : -1, (u64)inst << 12, + chan ? chan->object.client->name : "unknown", + mthd, data); + } + nvkm_fifo_chan_put(fifo, flags, &chan); + + nvkm_wr32(device, 0x102130, stat); + nvkm_wr32(device, 0x10200c, 0x10); +} + +static int +g84_cipher_init(struct nvkm_engine *cipher) +{ + struct nvkm_device *device = cipher->subdev.device; + nvkm_wr32(device, 0x102130, 0xffffffff); + nvkm_wr32(device, 0x102140, 0xffffffbf); + nvkm_wr32(device, 0x10200c, 0x00000010); + return 0; +} + +static const struct nvkm_engine_func +g84_cipher = { + .init = g84_cipher_init, + .intr = g84_cipher_intr, + .cclass = &g84_cipher_cclass, + .sclass = { + { -1, -1, NV74_CIPHER, &g84_cipher_oclass_func }, + {} + } +}; + +int +g84_cipher_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_engine_new_(&g84_cipher, device, type, inst, true, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild new file mode 100644 index 000000000..293c57678 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/Kbuild @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/device/acpi.o +nvkm-y += nvkm/engine/device/base.o +nvkm-y += nvkm/engine/device/ctrl.o +nvkm-y += nvkm/engine/device/pci.o +nvkm-y += nvkm/engine/device/tegra.o +nvkm-y += nvkm/engine/device/user.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c new file mode 100644 index 000000000..c948a0dc9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c @@ -0,0 +1,58 @@ +/* + * Copyright 2014 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 "acpi.h" + +#include +#include + +#ifdef CONFIG_ACPI +static int +nvkm_acpi_ntfy(struct notifier_block *nb, unsigned long val, void *data) +{ + struct nvkm_device *device = container_of(nb, typeof(*device), acpi.nb); + struct acpi_bus_event *info = data; + + if (!strcmp(info->device_class, "ac_adapter")) + nvkm_clk_pwrsrc(device); + + return NOTIFY_DONE; +} +#endif + +void +nvkm_acpi_fini(struct nvkm_device *device) +{ +#ifdef CONFIG_ACPI + unregister_acpi_notifier(&device->acpi.nb); +#endif +} + +void +nvkm_acpi_init(struct nvkm_device *device) +{ +#ifdef CONFIG_ACPI + device->acpi.nb.notifier_call = nvkm_acpi_ntfy; + register_acpi_notifier(&device->acpi.nb); +#endif +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h b/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h new file mode 100644 index 000000000..1d3c5cf7c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DEVICE_ACPI_H__ +#define __NVKM_DEVICE_ACPI_H__ +#include +struct nvkm_device; + +void nvkm_acpi_init(struct nvkm_device *); +void nvkm_acpi_fini(struct nvkm_device *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c new file mode 100644 index 000000000..d8cf71fb0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -0,0 +1,3196 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "acpi.h" + +#include + +#include +#include + +static DEFINE_MUTEX(nv_devices_mutex); +static LIST_HEAD(nv_devices); + +static struct nvkm_device * +nvkm_device_find_locked(u64 handle) +{ + struct nvkm_device *device; + list_for_each_entry(device, &nv_devices, head) { + if (device->handle == handle) + return device; + } + return NULL; +} + +struct nvkm_device * +nvkm_device_find(u64 handle) +{ + struct nvkm_device *device; + mutex_lock(&nv_devices_mutex); + device = nvkm_device_find_locked(handle); + mutex_unlock(&nv_devices_mutex); + return device; +} + +int +nvkm_device_list(u64 *name, int size) +{ + struct nvkm_device *device; + int nr = 0; + mutex_lock(&nv_devices_mutex); + list_for_each_entry(device, &nv_devices, head) { + if (nr++ < size) + name[nr - 1] = device->handle; + } + mutex_unlock(&nv_devices_mutex); + return nr; +} + +static const struct nvkm_device_chip +null_chipset = { + .name = "NULL", + .bios = { 0x00000001, nvkm_bios_new }, +}; + +static const struct nvkm_device_chip +nv4_chipset = { + .name = "NV04", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv04_devinit_new }, + .fb = { 0x00000001, nv04_fb_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv04_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv04_fifo_new }, + .gr = { 0x00000001, nv04_gr_new }, + .sw = { 0x00000001, nv04_sw_new }, +}; + +static const struct nvkm_device_chip +nv5_chipset = { + .name = "NV05", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv05_devinit_new }, + .fb = { 0x00000001, nv04_fb_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv04_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv04_fifo_new }, + .gr = { 0x00000001, nv04_gr_new }, + .sw = { 0x00000001, nv04_sw_new }, +}; + +static const struct nvkm_device_chip +nv10_chipset = { + .name = "NV10", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv10_devinit_new }, + .fb = { 0x00000001, nv10_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv04_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .gr = { 0x00000001, nv10_gr_new }, +}; + +static const struct nvkm_device_chip +nv11_chipset = { + .name = "NV11", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv10_devinit_new }, + .fb = { 0x00000001, nv10_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv11_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv10_fifo_new }, + .gr = { 0x00000001, nv15_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv15_chipset = { + .name = "NV15", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv10_devinit_new }, + .fb = { 0x00000001, nv10_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv04_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv10_fifo_new }, + .gr = { 0x00000001, nv15_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv17_chipset = { + .name = "NV17", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv10_devinit_new }, + .fb = { 0x00000001, nv10_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv17_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv18_chipset = { + .name = "NV18", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv10_devinit_new }, + .fb = { 0x00000001, nv10_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv17_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv1a_chipset = { + .name = "nForce", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv1a_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv04_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv10_fifo_new }, + .gr = { 0x00000001, nv15_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv1f_chipset = { + .name = "nForce2", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv1a_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv17_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv20_chipset = { + .name = "NV20", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv20_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv20_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv25_chipset = { + .name = "NV25", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv25_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv25_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv28_chipset = { + .name = "NV28", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv25_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv25_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv2a_chipset = { + .name = "NV2A", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv25_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv2a_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv30_chipset = { + .name = "NV30", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv30_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv30_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv31_chipset = { + .name = "NV31", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv30_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv30_gr_new }, + .mpeg = { 0x00000001, nv31_mpeg_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv34_chipset = { + .name = "NV34", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv10_devinit_new }, + .fb = { 0x00000001, nv10_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv34_gr_new }, + .mpeg = { 0x00000001, nv31_mpeg_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv35_chipset = { + .name = "NV35", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv04_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv35_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv35_gr_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv36_chipset = { + .name = "NV36", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv04_clk_new }, + .devinit = { 0x00000001, nv20_devinit_new }, + .fb = { 0x00000001, nv36_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv04_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv04_pci_new }, + .timer = { 0x00000001, nv04_timer_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv17_fifo_new }, + .gr = { 0x00000001, nv35_gr_new }, + .mpeg = { 0x00000001, nv31_mpeg_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv40_chipset = { + .name = "NV40", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv40_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv40_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv40_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv41_chipset = { + .name = "NV41", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv41_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv41_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv40_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv42_chipset = { + .name = "NV42", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv41_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv41_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv40_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv43_chipset = { + .name = "NV43", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv41_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv41_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv40_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv44_chipset = { + .name = "NV44", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv44_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv44_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv45_chipset = { + .name = "NV45", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv40_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv46_chipset = { + .name = "G72", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv46_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv44_mmu_new }, + .pci = { 0x00000001, nv46_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv47_chipset = { + .name = "G70", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv47_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv41_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv49_chipset = { + .name = "G71", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv49_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv41_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv4a_chipset = { + .name = "NV44A", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv44_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv04_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv4b_chipset = { + .name = "G73", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv49_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv17_mc_new }, + .mmu = { 0x00000001, nv41_mmu_new }, + .pci = { 0x00000001, nv40_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv40_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv4c_chipset = { + .name = "C61", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv46_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv44_mmu_new }, + .pci = { 0x00000001, nv4c_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv4e_chipset = { + .name = "C51", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv4e_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv4e_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv44_mmu_new }, + .pci = { 0x00000001, nv4c_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv50_chipset = { + .name = "G80", + .bar = { 0x00000001, nv50_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv50_bus_new }, + .clk = { 0x00000001, nv50_clk_new }, + .devinit = { 0x00000001, nv50_devinit_new }, + .fb = { 0x00000001, nv50_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, nv50_gpio_new }, + .i2c = { 0x00000001, nv50_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, nv50_mc_new }, + .mmu = { 0x00000001, nv50_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, nv46_pci_new }, + .therm = { 0x00000001, nv50_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv50_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, nv50_fifo_new }, + .gr = { 0x00000001, nv50_gr_new }, + .mpeg = { 0x00000001, nv50_mpeg_new }, + .pm = { 0x00000001, nv50_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nv63_chipset = { + .name = "C73", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv46_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv44_mmu_new }, + .pci = { 0x00000001, nv4c_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv67_chipset = { + .name = "C67", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv46_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv44_mmu_new }, + .pci = { 0x00000001, nv4c_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv68_chipset = { + .name = "C68", + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv31_bus_new }, + .clk = { 0x00000001, nv40_clk_new }, + .devinit = { 0x00000001, nv1a_devinit_new }, + .fb = { 0x00000001, nv46_fb_new }, + .gpio = { 0x00000001, nv10_gpio_new }, + .i2c = { 0x00000001, nv04_i2c_new }, + .imem = { 0x00000001, nv40_instmem_new }, + .mc = { 0x00000001, nv44_mc_new }, + .mmu = { 0x00000001, nv44_mmu_new }, + .pci = { 0x00000001, nv4c_pci_new }, + .therm = { 0x00000001, nv40_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, nv04_disp_new }, + .dma = { 0x00000001, nv04_dma_new }, + .fifo = { 0x00000001, nv40_fifo_new }, + .gr = { 0x00000001, nv44_gr_new }, + .mpeg = { 0x00000001, nv44_mpeg_new }, + .pm = { 0x00000001, nv40_pm_new }, + .sw = { 0x00000001, nv10_sw_new }, +}; + +static const struct nvkm_device_chip +nv84_chipset = { + .name = "G84", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv50_bus_new }, + .clk = { 0x00000001, g84_clk_new }, + .devinit = { 0x00000001, g84_devinit_new }, + .fb = { 0x00000001, g84_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, nv50_gpio_new }, + .i2c = { 0x00000001, nv50_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g84_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g84_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .bsp = { 0x00000001, g84_bsp_new }, + .cipher = { 0x00000001, g84_cipher_new }, + .disp = { 0x00000001, g84_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, g84_gr_new }, + .mpeg = { 0x00000001, g84_mpeg_new }, + .pm = { 0x00000001, g84_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, + .vp = { 0x00000001, g84_vp_new }, +}; + +static const struct nvkm_device_chip +nv86_chipset = { + .name = "G86", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv50_bus_new }, + .clk = { 0x00000001, g84_clk_new }, + .devinit = { 0x00000001, g84_devinit_new }, + .fb = { 0x00000001, g84_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, nv50_gpio_new }, + .i2c = { 0x00000001, nv50_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g84_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g84_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .bsp = { 0x00000001, g84_bsp_new }, + .cipher = { 0x00000001, g84_cipher_new }, + .disp = { 0x00000001, g84_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, g84_gr_new }, + .mpeg = { 0x00000001, g84_mpeg_new }, + .pm = { 0x00000001, g84_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, + .vp = { 0x00000001, g84_vp_new }, +}; + +static const struct nvkm_device_chip +nv92_chipset = { + .name = "G92", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, nv50_bus_new }, + .clk = { 0x00000001, g84_clk_new }, + .devinit = { 0x00000001, g84_devinit_new }, + .fb = { 0x00000001, g84_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, nv50_gpio_new }, + .i2c = { 0x00000001, nv50_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g84_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g92_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .bsp = { 0x00000001, g84_bsp_new }, + .cipher = { 0x00000001, g84_cipher_new }, + .disp = { 0x00000001, g84_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, g84_gr_new }, + .mpeg = { 0x00000001, g84_mpeg_new }, + .pm = { 0x00000001, g84_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, + .vp = { 0x00000001, g84_vp_new }, +}; + +static const struct nvkm_device_chip +nv94_chipset = { + .name = "G94", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, g84_clk_new }, + .devinit = { 0x00000001, g84_devinit_new }, + .fb = { 0x00000001, g84_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g84_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .bsp = { 0x00000001, g84_bsp_new }, + .cipher = { 0x00000001, g84_cipher_new }, + .disp = { 0x00000001, g94_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, g84_gr_new }, + .mpeg = { 0x00000001, g84_mpeg_new }, + .pm = { 0x00000001, g84_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, + .vp = { 0x00000001, g84_vp_new }, +}; + +static const struct nvkm_device_chip +nv96_chipset = { + .name = "G96", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, g84_clk_new }, + .devinit = { 0x00000001, g84_devinit_new }, + .fb = { 0x00000001, g84_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g84_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .bsp = { 0x00000001, g84_bsp_new }, + .cipher = { 0x00000001, g84_cipher_new }, + .disp = { 0x00000001, g94_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, g84_gr_new }, + .mpeg = { 0x00000001, g84_mpeg_new }, + .pm = { 0x00000001, g84_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, + .vp = { 0x00000001, g84_vp_new }, +}; + +static const struct nvkm_device_chip +nv98_chipset = { + .name = "G98", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, g84_clk_new }, + .devinit = { 0x00000001, g98_devinit_new }, + .fb = { 0x00000001, g84_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g98_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, g94_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, g84_gr_new }, + .mspdec = { 0x00000001, g98_mspdec_new }, + .msppp = { 0x00000001, g98_msppp_new }, + .msvld = { 0x00000001, g98_msvld_new }, + .pm = { 0x00000001, g84_pm_new }, + .sec = { 0x00000001, g98_sec_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nva0_chipset = { + .name = "GT200", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, g84_clk_new }, + .devinit = { 0x00000001, g84_devinit_new }, + .fb = { 0x00000001, g84_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, nv50_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g84_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .bsp = { 0x00000001, g84_bsp_new }, + .cipher = { 0x00000001, g84_cipher_new }, + .disp = { 0x00000001, gt200_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, gt200_gr_new }, + .mpeg = { 0x00000001, g84_mpeg_new }, + .pm = { 0x00000001, gt200_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, + .vp = { 0x00000001, g84_vp_new }, +}; + +static const struct nvkm_device_chip +nva3_chipset = { + .name = "GT215", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, gt215_clk_new }, + .devinit = { 0x00000001, gt215_devinit_new }, + .fb = { 0x00000001, gt215_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, gt215_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .pmu = { 0x00000001, gt215_pmu_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .ce = { 0x00000001, gt215_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, gt215_gr_new }, + .mpeg = { 0x00000001, g84_mpeg_new }, + .mspdec = { 0x00000001, gt215_mspdec_new }, + .msppp = { 0x00000001, gt215_msppp_new }, + .msvld = { 0x00000001, gt215_msvld_new }, + .pm = { 0x00000001, gt215_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nva5_chipset = { + .name = "GT216", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, gt215_clk_new }, + .devinit = { 0x00000001, gt215_devinit_new }, + .fb = { 0x00000001, gt215_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, gt215_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .pmu = { 0x00000001, gt215_pmu_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .ce = { 0x00000001, gt215_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, gt215_gr_new }, + .mspdec = { 0x00000001, gt215_mspdec_new }, + .msppp = { 0x00000001, gt215_msppp_new }, + .msvld = { 0x00000001, gt215_msvld_new }, + .pm = { 0x00000001, gt215_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nva8_chipset = { + .name = "GT218", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, gt215_clk_new }, + .devinit = { 0x00000001, gt215_devinit_new }, + .fb = { 0x00000001, gt215_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, gt215_mc_new }, + .mmu = { 0x00000001, g84_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .pmu = { 0x00000001, gt215_pmu_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .ce = { 0x00000001, gt215_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, gt215_gr_new }, + .mspdec = { 0x00000001, gt215_mspdec_new }, + .msppp = { 0x00000001, gt215_msppp_new }, + .msvld = { 0x00000001, gt215_msvld_new }, + .pm = { 0x00000001, gt215_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nvaa_chipset = { + .name = "MCP77/MCP78", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, mcp77_clk_new }, + .devinit = { 0x00000001, g98_devinit_new }, + .fb = { 0x00000001, mcp77_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g98_mc_new }, + .mmu = { 0x00000001, mcp77_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, mcp77_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, gt200_gr_new }, + .mspdec = { 0x00000001, g98_mspdec_new }, + .msppp = { 0x00000001, g98_msppp_new }, + .msvld = { 0x00000001, g98_msvld_new }, + .pm = { 0x00000001, g84_pm_new }, + .sec = { 0x00000001, g98_sec_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nvac_chipset = { + .name = "MCP79/MCP7A", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, mcp77_clk_new }, + .devinit = { 0x00000001, g98_devinit_new }, + .fb = { 0x00000001, mcp77_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, g98_mc_new }, + .mmu = { 0x00000001, mcp77_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .therm = { 0x00000001, g84_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .disp = { 0x00000001, mcp77_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, mcp79_gr_new }, + .mspdec = { 0x00000001, g98_mspdec_new }, + .msppp = { 0x00000001, g98_msppp_new }, + .msvld = { 0x00000001, g98_msvld_new }, + .pm = { 0x00000001, g84_pm_new }, + .sec = { 0x00000001, g98_sec_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nvaf_chipset = { + .name = "MCP89", + .bar = { 0x00000001, g84_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, g94_bus_new }, + .clk = { 0x00000001, gt215_clk_new }, + .devinit = { 0x00000001, mcp89_devinit_new }, + .fb = { 0x00000001, mcp89_fb_new }, + .fuse = { 0x00000001, nv50_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, gt215_mc_new }, + .mmu = { 0x00000001, mcp77_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, g94_pci_new }, + .pmu = { 0x00000001, gt215_pmu_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, nv40_volt_new }, + .ce = { 0x00000001, gt215_ce_new }, + .disp = { 0x00000001, mcp89_disp_new }, + .dma = { 0x00000001, nv50_dma_new }, + .fifo = { 0x00000001, g84_fifo_new }, + .gr = { 0x00000001, mcp89_gr_new }, + .mspdec = { 0x00000001, gt215_mspdec_new }, + .msppp = { 0x00000001, gt215_msppp_new }, + .msvld = { 0x00000001, mcp89_msvld_new }, + .pm = { 0x00000001, gt215_pm_new }, + .sw = { 0x00000001, nv50_sw_new }, +}; + +static const struct nvkm_device_chip +nvc0_chipset = { + .name = "GF100", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf100_pci_new }, + .pmu = { 0x00000001, gf100_pmu_new }, + .privring = { 0x00000001, gf100_privring_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000003, gf100_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, gf100_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf100_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf100_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvc1_chipset = { + .name = "GF108", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf108_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf106_pci_new }, + .pmu = { 0x00000001, gf100_pmu_new }, + .privring = { 0x00000001, gf100_privring_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000001, gf100_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, gf100_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf108_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf108_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvc3_chipset = { + .name = "GF106", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf106_pci_new }, + .pmu = { 0x00000001, gf100_pmu_new }, + .privring = { 0x00000001, gf100_privring_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000001, gf100_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, gf100_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf104_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf100_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvc4_chipset = { + .name = "GF104", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf100_pci_new }, + .pmu = { 0x00000001, gf100_pmu_new }, + .privring = { 0x00000001, gf100_privring_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000003, gf100_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, gf100_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf104_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf100_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvc8_chipset = { + .name = "GF110", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf100_pci_new }, + .pmu = { 0x00000001, gf100_pmu_new }, + .privring = { 0x00000001, gf100_privring_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000003, gf100_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, gf100_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf110_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf100_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvce_chipset = { + .name = "GF114", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf100_pci_new }, + .pmu = { 0x00000001, gf100_pmu_new }, + .privring = { 0x00000001, gf100_privring_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000003, gf100_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, gf100_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf104_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf100_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvcf_chipset = { + .name = "GF116", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, g94_gpio_new }, + .i2c = { 0x00000001, g94_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf106_pci_new }, + .pmu = { 0x00000001, gf100_pmu_new }, + .privring = { 0x00000001, gf100_privring_new }, + .therm = { 0x00000001, gt215_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000001, gf100_ce_new }, + .disp = { 0x00000001, gt215_disp_new }, + .dma = { 0x00000001, gf100_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf104_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf100_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvd7_chipset = { + .name = "GF117", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gf119_gpio_new }, + .i2c = { 0x00000001, gf117_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf106_pci_new }, + .privring = { 0x00000001, gf117_privring_new }, + .therm = { 0x00000001, gf119_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf117_volt_new }, + .ce = { 0x00000001, gf100_ce_new }, + .disp = { 0x00000001, gf119_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf117_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf117_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvd9_chipset = { + .name = "GF119", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gf100_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gf100_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gf119_gpio_new }, + .i2c = { 0x00000001, gf119_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gf100_ltc_new }, + .mc = { 0x00000001, gf100_mc_new }, + .mmu = { 0x00000001, gf100_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gf106_pci_new }, + .pmu = { 0x00000001, gf119_pmu_new }, + .privring = { 0x00000001, gf117_privring_new }, + .therm = { 0x00000001, gf119_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .volt = { 0x00000001, gf100_volt_new }, + .ce = { 0x00000001, gf100_ce_new }, + .disp = { 0x00000001, gf119_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gf100_fifo_new }, + .gr = { 0x00000001, gf119_gr_new }, + .mspdec = { 0x00000001, gf100_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gf100_msvld_new }, + .pm = { 0x00000001, gf117_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nve4_chipset = { + .name = "GK104", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gk104_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk104_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk104_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gk104_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gk104_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gk104_ce_new }, + .disp = { 0x00000001, gk104_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk104_fifo_new }, + .gr = { 0x00000001, gk104_gr_new }, + .mspdec = { 0x00000001, gk104_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gk104_msvld_new }, + .pm = { 0x00000001, gk104_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nve6_chipset = { + .name = "GK106", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gk104_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk104_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk104_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gk104_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gk104_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gk104_ce_new }, + .disp = { 0x00000001, gk104_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk104_fifo_new }, + .gr = { 0x00000001, gk104_gr_new }, + .mspdec = { 0x00000001, gk104_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gk104_msvld_new }, + .pm = { 0x00000001, gk104_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nve7_chipset = { + .name = "GK107", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gk104_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk104_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk104_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gk104_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gk104_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gk104_ce_new }, + .disp = { 0x00000001, gk104_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk104_fifo_new }, + .gr = { 0x00000001, gk104_gr_new }, + .mspdec = { 0x00000001, gk104_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gk104_msvld_new }, + .pm = { 0x00000001, gk104_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvea_chipset = { + .name = "GK20A", + .bar = { 0x00000001, gk20a_bar_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk20a_clk_new }, + .fb = { 0x00000001, gk20a_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .imem = { 0x00000001, gk20a_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gk20a_mmu_new }, + .pmu = { 0x00000001, gk20a_pmu_new }, + .privring = { 0x00000001, gk20a_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk20a_volt_new }, + .ce = { 0x00000004, gk104_ce_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk20a_fifo_new }, + .gr = { 0x00000001, gk20a_gr_new }, + .pm = { 0x00000001, gk104_pm_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvf0_chipset = { + .name = "GK110", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gk110_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk110_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk104_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gk110_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gk104_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gk104_ce_new }, + .disp = { 0x00000001, gk110_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk110_fifo_new }, + .gr = { 0x00000001, gk110_gr_new }, + .mspdec = { 0x00000001, gk104_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gk104_msvld_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nvf1_chipset = { + .name = "GK110B", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gk110_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk110_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk104_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gk110_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gk104_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gk104_ce_new }, + .disp = { 0x00000001, gk110_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk110_fifo_new }, + .gr = { 0x00000001, gk110b_gr_new }, + .mspdec = { 0x00000001, gk104_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gk104_msvld_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv106_chipset = { + .name = "GK208B", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gk110_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk110_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gk208_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gk104_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gk104_ce_new }, + .disp = { 0x00000001, gk110_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk208_fifo_new }, + .gr = { 0x00000001, gk208_gr_new }, + .mspdec = { 0x00000001, gk104_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gk104_msvld_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv108_chipset = { + .name = "GK208", + .bar = { 0x00000001, gf100_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gf100_devinit_new }, + .fb = { 0x00000001, gk110_fb_new }, + .fuse = { 0x00000001, gf100_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk110_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gk104_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gk208_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gk104_therm_new }, + .timer = { 0x00000001, nv41_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gk104_ce_new }, + .disp = { 0x00000001, gk110_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gk208_fifo_new }, + .gr = { 0x00000001, gk208_gr_new }, + .mspdec = { 0x00000001, gk104_mspdec_new }, + .msppp = { 0x00000001, gf100_msppp_new }, + .msvld = { 0x00000001, gk104_msvld_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv117_chipset = { + .name = "GM107", + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gm107_devinit_new }, + .fb = { 0x00000001, gm107_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk110_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gm107_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gm107_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gm107_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000005, gm107_ce_new }, + .disp = { 0x00000001, gm107_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gm107_fifo_new }, + .gr = { 0x00000001, gm107_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv118_chipset = { + .name = "GM108", + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gk104_clk_new }, + .devinit = { 0x00000001, gm107_devinit_new }, + .fb = { 0x00000001, gm107_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gk110_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gm107_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gk104_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gm107_pmu_new }, + .privring = { 0x00000001, gk104_privring_new }, + .therm = { 0x00000001, gm107_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000005, gm107_ce_new }, + .disp = { 0x00000001, gm107_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gm107_fifo_new }, + .gr = { 0x00000001, gm107_gr_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv120_chipset = { + .name = "GM200", + .acr = { 0x00000001, gm200_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fb = { 0x00000001, gm200_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gm200_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gm200_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gm200_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gm200_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gm200_ce_new }, + .disp = { 0x00000001, gm200_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gm200_fifo_new }, + .gr = { 0x00000001, gm200_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000003, gm107_nvenc_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv124_chipset = { + .name = "GM204", + .acr = { 0x00000001, gm200_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fb = { 0x00000001, gm200_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gm200_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gm200_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gm200_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gm200_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gm200_ce_new }, + .disp = { 0x00000001, gm200_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gm200_fifo_new }, + .gr = { 0x00000001, gm200_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000003, gm107_nvenc_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv126_chipset = { + .name = "GM206", + .acr = { 0x00000001, gm200_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fb = { 0x00000001, gm200_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .iccsense = { 0x00000001, gf100_iccsense_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gm200_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gm200_mmu_new }, + .mxm = { 0x00000001, nv50_mxm_new }, + .pci = { 0x00000001, gk104_pci_new }, + .pmu = { 0x00000001, gm200_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gm200_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gk104_volt_new }, + .ce = { 0x00000007, gm200_ce_new }, + .disp = { 0x00000001, gm200_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gm200_fifo_new }, + .gr = { 0x00000001, gm200_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv12b_chipset = { + .name = "GM20B", + .acr = { 0x00000001, gm20b_acr_new }, + .bar = { 0x00000001, gm20b_bar_new }, + .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gm20b_clk_new }, + .fb = { 0x00000001, gm20b_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .imem = { 0x00000001, gk20a_instmem_new }, + .ltc = { 0x00000001, gm200_ltc_new }, + .mc = { 0x00000001, gk20a_mc_new }, + .mmu = { 0x00000001, gm20b_mmu_new }, + .pmu = { 0x00000001, gm20b_pmu_new }, + .privring = { 0x00000001, gk20a_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .volt = { 0x00000001, gm20b_volt_new }, + .ce = { 0x00000004, gm200_ce_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gm20b_fifo_new }, + .gr = { 0x00000001, gm20b_gr_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv130_chipset = { + .name = "GP100", + .acr = { 0x00000001, gm200_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fault = { 0x00000001, gp100_fault_new }, + .fb = { 0x00000001, gp100_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp100_ltc_new }, + .mc = { 0x00000001, gp100_mc_new }, + .mmu = { 0x00000001, gp100_mmu_new }, + .therm = { 0x00000001, gp100_therm_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gm200_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000003f, gp100_ce_new }, + .dma = { 0x00000001, gf119_dma_new }, + .disp = { 0x00000001, gp100_disp_new }, + .fifo = { 0x00000001, gp100_fifo_new }, + .gr = { 0x00000001, gp100_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000007, gm107_nvenc_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv132_chipset = { + .name = "GP102", + .acr = { 0x00000001, gp102_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fault = { 0x00000001, gp100_fault_new }, + .fb = { 0x00000001, gp102_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, gp100_mc_new }, + .mmu = { 0x00000001, gp100_mmu_new }, + .therm = { 0x00000001, gp100_therm_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000000f, gp102_ce_new }, + .disp = { 0x00000001, gp102_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gp100_fifo_new }, + .gr = { 0x00000001, gp102_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000003, gm107_nvenc_new }, + .sec2 = { 0x00000001, gp102_sec2_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv134_chipset = { + .name = "GP104", + .acr = { 0x00000001, gp102_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fault = { 0x00000001, gp100_fault_new }, + .fb = { 0x00000001, gp102_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, gp100_mc_new }, + .mmu = { 0x00000001, gp100_mmu_new }, + .therm = { 0x00000001, gp100_therm_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000000f, gp102_ce_new }, + .disp = { 0x00000001, gp102_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gp100_fifo_new }, + .gr = { 0x00000001, gp104_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000003, gm107_nvenc_new }, + .sec2 = { 0x00000001, gp102_sec2_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv136_chipset = { + .name = "GP106", + .acr = { 0x00000001, gp102_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fault = { 0x00000001, gp100_fault_new }, + .fb = { 0x00000001, gp102_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, gp100_mc_new }, + .mmu = { 0x00000001, gp100_mmu_new }, + .therm = { 0x00000001, gp100_therm_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000000f, gp102_ce_new }, + .disp = { 0x00000001, gp102_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gp100_fifo_new }, + .gr = { 0x00000001, gp104_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sec2 = { 0x00000001, gp102_sec2_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv137_chipset = { + .name = "GP107", + .acr = { 0x00000001, gp102_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fault = { 0x00000001, gp100_fault_new }, + .fb = { 0x00000001, gp102_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, gp100_mc_new }, + .mmu = { 0x00000001, gp100_mmu_new }, + .therm = { 0x00000001, gp100_therm_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000000f, gp102_ce_new }, + .disp = { 0x00000001, gp102_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gp100_fifo_new }, + .gr = { 0x00000001, gp107_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000003, gm107_nvenc_new }, + .sec2 = { 0x00000001, gp102_sec2_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv138_chipset = { + .name = "GP108", + .acr = { 0x00000001, gp108_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gm200_devinit_new }, + .fault = { 0x00000001, gp100_fault_new }, + .fb = { 0x00000001, gp102_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, gp100_mc_new }, + .mmu = { 0x00000001, gp100_mmu_new }, + .therm = { 0x00000001, gp100_therm_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000000f, gp102_ce_new }, + .disp = { 0x00000001, gp102_disp_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gp100_fifo_new }, + .gr = { 0x00000001, gp108_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .sec2 = { 0x00000001, gp108_sec2_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv13b_chipset = { + .name = "GP10B", + .acr = { 0x00000001, gp10b_acr_new }, + .bar = { 0x00000001, gm20b_bar_new }, + .bus = { 0x00000001, gf100_bus_new }, + .fault = { 0x00000001, gp10b_fault_new }, + .fb = { 0x00000001, gp10b_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .imem = { 0x00000001, gk20a_instmem_new }, + .ltc = { 0x00000001, gp10b_ltc_new }, + .mc = { 0x00000001, gp10b_mc_new }, + .mmu = { 0x00000001, gp10b_mmu_new }, + .pmu = { 0x00000001, gp10b_pmu_new }, + .privring = { 0x00000001, gp10b_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x00000001, gp100_ce_new }, + .dma = { 0x00000001, gf119_dma_new }, + .fifo = { 0x00000001, gp10b_fifo_new }, + .gr = { 0x00000001, gp10b_gr_new }, + .sw = { 0x00000001, gf100_sw_new }, +}; + +static const struct nvkm_device_chip +nv140_chipset = { + .name = "GV100", + .acr = { 0x00000001, gp108_acr_new }, + .bar = { 0x00000001, gm107_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, gv100_devinit_new }, + .fault = { 0x00000001, gv100_fault_new }, + .fb = { 0x00000001, gv100_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .gsp = { 0x00000001, gv100_gsp_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, gp100_mc_new }, + .mmu = { 0x00000001, gv100_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gp100_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x000001ff, gv100_ce_new }, + .disp = { 0x00000001, gv100_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, gv100_fifo_new }, + .gr = { 0x00000001, gv100_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000007, gm107_nvenc_new }, + .sec2 = { 0x00000001, gp108_sec2_new }, +}; + +static const struct nvkm_device_chip +nv162_chipset = { + .name = "TU102", + .acr = { 0x00000001, tu102_acr_new }, + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, tu102_devinit_new }, + .fault = { 0x00000001, tu102_fault_new }, + .fb = { 0x00000001, gv100_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .gsp = { 0x00000001, gv100_gsp_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, tu102_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gp100_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000001f, tu102_ce_new }, + .disp = { 0x00000001, tu102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, tu102_fifo_new }, + .gr = { 0x00000001, tu102_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sec2 = { 0x00000001, tu102_sec2_new }, +}; + +static const struct nvkm_device_chip +nv164_chipset = { + .name = "TU104", + .acr = { 0x00000001, tu102_acr_new }, + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, tu102_devinit_new }, + .fault = { 0x00000001, tu102_fault_new }, + .fb = { 0x00000001, gv100_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .gsp = { 0x00000001, gv100_gsp_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, tu102_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gp100_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000001f, tu102_ce_new }, + .disp = { 0x00000001, tu102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, tu102_fifo_new }, + .gr = { 0x00000001, tu102_gr_new }, + .nvdec = { 0x00000003, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sec2 = { 0x00000001, tu102_sec2_new }, +}; + +static const struct nvkm_device_chip +nv166_chipset = { + .name = "TU106", + .acr = { 0x00000001, tu102_acr_new }, + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, tu102_devinit_new }, + .fault = { 0x00000001, tu102_fault_new }, + .fb = { 0x00000001, gv100_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .gsp = { 0x00000001, gv100_gsp_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, tu102_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gp100_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000001f, tu102_ce_new }, + .disp = { 0x00000001, tu102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, tu102_fifo_new }, + .gr = { 0x00000001, tu102_gr_new }, + .nvdec = { 0x00000007, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sec2 = { 0x00000001, tu102_sec2_new }, +}; + +static const struct nvkm_device_chip +nv167_chipset = { + .name = "TU117", + .acr = { 0x00000001, tu102_acr_new }, + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, tu102_devinit_new }, + .fault = { 0x00000001, tu102_fault_new }, + .fb = { 0x00000001, gv100_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .gsp = { 0x00000001, gv100_gsp_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, tu102_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gp100_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000001f, tu102_ce_new }, + .disp = { 0x00000001, tu102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, tu102_fifo_new }, + .gr = { 0x00000001, tu102_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sec2 = { 0x00000001, tu102_sec2_new }, +}; + +static const struct nvkm_device_chip +nv168_chipset = { + .name = "TU116", + .acr = { 0x00000001, tu102_acr_new }, + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .bus = { 0x00000001, gf100_bus_new }, + .devinit = { 0x00000001, tu102_devinit_new }, + .fault = { 0x00000001, tu102_fault_new }, + .fb = { 0x00000001, gv100_fb_new }, + .fuse = { 0x00000001, gm107_fuse_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .gsp = { 0x00000001, gv100_gsp_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .ltc = { 0x00000001, gp102_ltc_new }, + .mc = { 0x00000001, tu102_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .pmu = { 0x00000001, gp102_pmu_new }, + .privring = { 0x00000001, gm200_privring_new }, + .therm = { 0x00000001, gp100_therm_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, gk104_top_new }, + .ce = { 0x0000001f, tu102_ce_new }, + .disp = { 0x00000001, tu102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, tu102_fifo_new }, + .gr = { 0x00000001, tu102_gr_new }, + .nvdec = { 0x00000001, gm107_nvdec_new }, + .nvenc = { 0x00000001, gm107_nvenc_new }, + .sec2 = { 0x00000001, tu102_sec2_new }, +}; + +static const struct nvkm_device_chip +nv170_chipset = { + .name = "GA100", + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .devinit = { 0x00000001, ga100_devinit_new }, + .fb = { 0x00000001, ga100_fb_new }, + .gpio = { 0x00000001, gk104_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, ga100_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, ga100_top_new }, +}; + +static const struct nvkm_device_chip +nv172_chipset = { + .name = "GA102", + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .devinit = { 0x00000001, ga100_devinit_new }, + .fb = { 0x00000001, ga102_fb_new }, + .gpio = { 0x00000001, ga102_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, ga100_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, ga100_top_new }, + .disp = { 0x00000001, ga102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, ga102_fifo_new }, +}; + +static const struct nvkm_device_chip +nv173_chipset = { + .name = "GA103", + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .devinit = { 0x00000001, ga100_devinit_new }, + .fb = { 0x00000001, ga102_fb_new }, + .gpio = { 0x00000001, ga102_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, ga100_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, ga100_top_new }, + .disp = { 0x00000001, ga102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, ga102_fifo_new }, +}; + +static const struct nvkm_device_chip +nv174_chipset = { + .name = "GA104", + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .devinit = { 0x00000001, ga100_devinit_new }, + .fb = { 0x00000001, ga102_fb_new }, + .gpio = { 0x00000001, ga102_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, ga100_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, ga100_top_new }, + .disp = { 0x00000001, ga102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, ga102_fifo_new }, +}; + +static const struct nvkm_device_chip +nv176_chipset = { + .name = "GA106", + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .devinit = { 0x00000001, ga100_devinit_new }, + .fb = { 0x00000001, ga102_fb_new }, + .gpio = { 0x00000001, ga102_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, ga100_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, ga100_top_new }, + .disp = { 0x00000001, ga102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, ga102_fifo_new }, +}; + +static const struct nvkm_device_chip +nv177_chipset = { + .name = "GA107", + .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, + .devinit = { 0x00000001, ga100_devinit_new }, + .fb = { 0x00000001, ga102_fb_new }, + .gpio = { 0x00000001, ga102_gpio_new }, + .i2c = { 0x00000001, gm200_i2c_new }, + .imem = { 0x00000001, nv50_instmem_new }, + .mc = { 0x00000001, ga100_mc_new }, + .mmu = { 0x00000001, tu102_mmu_new }, + .pci = { 0x00000001, gp100_pci_new }, + .privring = { 0x00000001, gm200_privring_new }, + .timer = { 0x00000001, gk20a_timer_new }, + .top = { 0x00000001, ga100_top_new }, + .disp = { 0x00000001, ga102_disp_new }, + .dma = { 0x00000001, gv100_dma_new }, + .fifo = { 0x00000001, ga102_fifo_new }, +}; + +struct nvkm_subdev * +nvkm_device_subdev(struct nvkm_device *device, int type, int inst) +{ + struct nvkm_subdev *subdev; + + list_for_each_entry(subdev, &device->subdev, head) { + if (subdev->type == type && subdev->inst == inst) + return subdev; + } + + return NULL; +} + +struct nvkm_engine * +nvkm_device_engine(struct nvkm_device *device, int type, int inst) +{ + struct nvkm_subdev *subdev = nvkm_device_subdev(device, type, inst); + if (subdev && subdev->func == &nvkm_engine) + return container_of(subdev, struct nvkm_engine, subdev); + return NULL; +} + +int +nvkm_device_fini(struct nvkm_device *device, bool suspend) +{ + const char *action = suspend ? "suspend" : "fini"; + struct nvkm_subdev *subdev; + int ret; + s64 time; + + nvdev_trace(device, "%s running...\n", action); + time = ktime_to_us(ktime_get()); + + nvkm_acpi_fini(device); + + list_for_each_entry_reverse(subdev, &device->subdev, head) { + ret = nvkm_subdev_fini(subdev, suspend); + if (ret && suspend) + goto fail; + } + + nvkm_therm_clkgate_fini(device->therm, suspend); + + if (device->func->fini) + device->func->fini(device, suspend); + + time = ktime_to_us(ktime_get()) - time; + nvdev_trace(device, "%s completed in %lldus...\n", action, time); + return 0; + +fail: + list_for_each_entry_from(subdev, &device->subdev, head) { + int rret = nvkm_subdev_init(subdev); + if (rret) + nvkm_fatal(subdev, "failed restart, %d\n", ret); + } + + nvdev_trace(device, "%s failed with %d\n", action, ret); + return ret; +} + +static int +nvkm_device_preinit(struct nvkm_device *device) +{ + struct nvkm_subdev *subdev; + int ret; + s64 time; + + nvdev_trace(device, "preinit running...\n"); + time = ktime_to_us(ktime_get()); + + if (device->func->preinit) { + ret = device->func->preinit(device); + if (ret) + goto fail; + } + + list_for_each_entry(subdev, &device->subdev, head) { + ret = nvkm_subdev_preinit(subdev); + if (ret) + goto fail; + } + + ret = nvkm_devinit_post(device->devinit); + if (ret) + goto fail; + + time = ktime_to_us(ktime_get()) - time; + nvdev_trace(device, "preinit completed in %lldus\n", time); + return 0; + +fail: + nvdev_error(device, "preinit failed with %d\n", ret); + return ret; +} + +int +nvkm_device_init(struct nvkm_device *device) +{ + struct nvkm_subdev *subdev; + int ret; + s64 time; + + ret = nvkm_device_preinit(device); + if (ret) + return ret; + + nvkm_device_fini(device, false); + + nvdev_trace(device, "init running...\n"); + time = ktime_to_us(ktime_get()); + + if (device->func->init) { + ret = device->func->init(device); + if (ret) + goto fail; + } + + list_for_each_entry(subdev, &device->subdev, head) { + ret = nvkm_subdev_init(subdev); + if (ret) + goto fail_subdev; + } + + nvkm_acpi_init(device); + nvkm_therm_clkgate_enable(device->therm); + + time = ktime_to_us(ktime_get()) - time; + nvdev_trace(device, "init completed in %lldus\n", time); + return 0; + +fail_subdev: + list_for_each_entry_from(subdev, &device->subdev, head) + nvkm_subdev_fini(subdev, false); +fail: + nvkm_device_fini(device, false); + + nvdev_error(device, "init failed with %d\n", ret); + return ret; +} + +void +nvkm_device_del(struct nvkm_device **pdevice) +{ + struct nvkm_device *device = *pdevice; + struct nvkm_subdev *subdev, *subtmp; + if (device) { + mutex_lock(&nv_devices_mutex); + + list_for_each_entry_safe_reverse(subdev, subtmp, &device->subdev, head) + nvkm_subdev_del(&subdev); + + if (device->pri) + iounmap(device->pri); + list_del(&device->head); + + if (device->func->dtor) + *pdevice = device->func->dtor(device); + mutex_unlock(&nv_devices_mutex); + + kfree(*pdevice); + *pdevice = NULL; + } +} + +/* returns true if the GPU is in the CPU native byte order */ +static inline bool +nvkm_device_endianness(struct nvkm_device *device) +{ +#ifdef __BIG_ENDIAN + const bool big_endian = true; +#else + const bool big_endian = false; +#endif + + /* Read NV_PMC_BOOT_1, and assume non-functional endian switch if it + * doesn't contain the expected values. + */ + u32 pmc_boot_1 = nvkm_rd32(device, 0x000004); + if (pmc_boot_1 && pmc_boot_1 != 0x01000001) + return !big_endian; /* Assume GPU is LE in this case. */ + + /* 0 means LE and 0x01000001 means BE GPU. Condition is true when + * GPU/CPU endianness don't match. + */ + if (big_endian == !pmc_boot_1) { + nvkm_wr32(device, 0x000004, 0x01000001); + nvkm_rd32(device, 0x000000); + if (nvkm_rd32(device, 0x000004) != (big_endian ? 0x01000001 : 0x00000000)) + return !big_endian; /* Assume GPU is LE on any unexpected read-back. */ + } + + /* CPU/GPU endianness should (hopefully) match. */ + return true; +} + +int +nvkm_device_ctor(const struct nvkm_device_func *func, + const struct nvkm_device_quirk *quirk, + struct device *dev, enum nvkm_device_type type, u64 handle, + const char *name, const char *cfg, const char *dbg, + bool detect, bool mmio, u64 subdev_mask, + struct nvkm_device *device) +{ + struct nvkm_subdev *subdev; + u64 mmio_base, mmio_size; + u32 boot0, boot1, strap; + int ret = -EEXIST, j; + unsigned chipset; + + mutex_lock(&nv_devices_mutex); + if (nvkm_device_find_locked(handle)) + goto done; + + device->func = func; + device->quirk = quirk; + device->dev = dev; + device->type = type; + device->handle = handle; + device->cfgopt = cfg; + device->dbgopt = dbg; + device->name = name; + list_add_tail(&device->head, &nv_devices); + device->debug = nvkm_dbgopt(device->dbgopt, "device"); + INIT_LIST_HEAD(&device->subdev); + + mmio_base = device->func->resource_addr(device, 0); + mmio_size = device->func->resource_size(device, 0); + + if (detect || mmio) { + device->pri = ioremap(mmio_base, mmio_size); + if (device->pri == NULL) { + nvdev_error(device, "unable to map PRI\n"); + ret = -ENOMEM; + goto done; + } + } + + /* identify the chipset, and determine classes of subdev/engines */ + if (detect) { + /* switch mmio to cpu's native endianness */ + if (!nvkm_device_endianness(device)) { + nvdev_error(device, + "Couldn't switch GPU to CPUs endianness\n"); + ret = -ENOSYS; + goto done; + } + + boot0 = nvkm_rd32(device, 0x000000); + + /* chipset can be overridden for devel/testing purposes */ + chipset = nvkm_longopt(device->cfgopt, "NvChipset", 0); + if (chipset) { + u32 override_boot0; + + if (chipset >= 0x10) { + override_boot0 = ((chipset & 0x1ff) << 20); + override_boot0 |= 0x000000a1; + } else { + if (chipset != 0x04) + override_boot0 = 0x20104000; + else + override_boot0 = 0x20004000; + } + + nvdev_warn(device, "CHIPSET OVERRIDE: %08x -> %08x\n", + boot0, override_boot0); + boot0 = override_boot0; + } + + /* determine chipset and derive architecture from it */ + if ((boot0 & 0x1f000000) > 0) { + device->chipset = (boot0 & 0x1ff00000) >> 20; + device->chiprev = (boot0 & 0x000000ff); + switch (device->chipset & 0x1f0) { + case 0x010: { + if (0x461 & (1 << (device->chipset & 0xf))) + device->card_type = NV_10; + else + device->card_type = NV_11; + device->chiprev = 0x00; + break; + } + case 0x020: device->card_type = NV_20; break; + case 0x030: device->card_type = NV_30; break; + case 0x040: + case 0x060: device->card_type = NV_40; break; + case 0x050: + case 0x080: + case 0x090: + case 0x0a0: device->card_type = NV_50; break; + case 0x0c0: + case 0x0d0: device->card_type = NV_C0; break; + case 0x0e0: + case 0x0f0: + case 0x100: device->card_type = NV_E0; break; + case 0x110: + case 0x120: device->card_type = GM100; break; + case 0x130: device->card_type = GP100; break; + case 0x140: device->card_type = GV100; break; + case 0x160: device->card_type = TU100; break; + case 0x170: device->card_type = GA100; break; + default: + break; + } + } else + if ((boot0 & 0xff00fff0) == 0x20004000) { + if (boot0 & 0x00f00000) + device->chipset = 0x05; + else + device->chipset = 0x04; + device->card_type = NV_04; + } + + switch (device->chipset) { + case 0x004: device->chip = &nv4_chipset; break; + case 0x005: device->chip = &nv5_chipset; break; + case 0x010: device->chip = &nv10_chipset; break; + case 0x011: device->chip = &nv11_chipset; break; + case 0x015: device->chip = &nv15_chipset; break; + case 0x017: device->chip = &nv17_chipset; break; + case 0x018: device->chip = &nv18_chipset; break; + case 0x01a: device->chip = &nv1a_chipset; break; + case 0x01f: device->chip = &nv1f_chipset; break; + case 0x020: device->chip = &nv20_chipset; break; + case 0x025: device->chip = &nv25_chipset; break; + case 0x028: device->chip = &nv28_chipset; break; + case 0x02a: device->chip = &nv2a_chipset; break; + case 0x030: device->chip = &nv30_chipset; break; + case 0x031: device->chip = &nv31_chipset; break; + case 0x034: device->chip = &nv34_chipset; break; + case 0x035: device->chip = &nv35_chipset; break; + case 0x036: device->chip = &nv36_chipset; break; + case 0x040: device->chip = &nv40_chipset; break; + case 0x041: device->chip = &nv41_chipset; break; + case 0x042: device->chip = &nv42_chipset; break; + case 0x043: device->chip = &nv43_chipset; break; + case 0x044: device->chip = &nv44_chipset; break; + case 0x045: device->chip = &nv45_chipset; break; + case 0x046: device->chip = &nv46_chipset; break; + case 0x047: device->chip = &nv47_chipset; break; + case 0x049: device->chip = &nv49_chipset; break; + case 0x04a: device->chip = &nv4a_chipset; break; + case 0x04b: device->chip = &nv4b_chipset; break; + case 0x04c: device->chip = &nv4c_chipset; break; + case 0x04e: device->chip = &nv4e_chipset; break; + case 0x050: device->chip = &nv50_chipset; break; + case 0x063: device->chip = &nv63_chipset; break; + case 0x067: device->chip = &nv67_chipset; break; + case 0x068: device->chip = &nv68_chipset; break; + case 0x084: device->chip = &nv84_chipset; break; + case 0x086: device->chip = &nv86_chipset; break; + case 0x092: device->chip = &nv92_chipset; break; + case 0x094: device->chip = &nv94_chipset; break; + case 0x096: device->chip = &nv96_chipset; break; + case 0x098: device->chip = &nv98_chipset; break; + case 0x0a0: device->chip = &nva0_chipset; break; + case 0x0a3: device->chip = &nva3_chipset; break; + case 0x0a5: device->chip = &nva5_chipset; break; + case 0x0a8: device->chip = &nva8_chipset; break; + case 0x0aa: device->chip = &nvaa_chipset; break; + case 0x0ac: device->chip = &nvac_chipset; break; + case 0x0af: device->chip = &nvaf_chipset; break; + case 0x0c0: device->chip = &nvc0_chipset; break; + case 0x0c1: device->chip = &nvc1_chipset; break; + case 0x0c3: device->chip = &nvc3_chipset; break; + case 0x0c4: device->chip = &nvc4_chipset; break; + case 0x0c8: device->chip = &nvc8_chipset; break; + case 0x0ce: device->chip = &nvce_chipset; break; + case 0x0cf: device->chip = &nvcf_chipset; break; + case 0x0d7: device->chip = &nvd7_chipset; break; + case 0x0d9: device->chip = &nvd9_chipset; break; + case 0x0e4: device->chip = &nve4_chipset; break; + case 0x0e6: device->chip = &nve6_chipset; break; + case 0x0e7: device->chip = &nve7_chipset; break; + case 0x0ea: device->chip = &nvea_chipset; break; + case 0x0f0: device->chip = &nvf0_chipset; break; + case 0x0f1: device->chip = &nvf1_chipset; break; + case 0x106: device->chip = &nv106_chipset; break; + case 0x108: device->chip = &nv108_chipset; break; + case 0x117: device->chip = &nv117_chipset; break; + case 0x118: device->chip = &nv118_chipset; break; + case 0x120: device->chip = &nv120_chipset; break; + case 0x124: device->chip = &nv124_chipset; break; + case 0x126: device->chip = &nv126_chipset; break; + case 0x12b: device->chip = &nv12b_chipset; break; + case 0x130: device->chip = &nv130_chipset; break; + case 0x132: device->chip = &nv132_chipset; break; + case 0x134: device->chip = &nv134_chipset; break; + case 0x136: device->chip = &nv136_chipset; break; + case 0x137: device->chip = &nv137_chipset; break; + case 0x138: device->chip = &nv138_chipset; break; + case 0x13b: device->chip = &nv13b_chipset; break; + case 0x140: device->chip = &nv140_chipset; break; + case 0x162: device->chip = &nv162_chipset; break; + case 0x164: device->chip = &nv164_chipset; break; + case 0x166: device->chip = &nv166_chipset; break; + case 0x167: device->chip = &nv167_chipset; break; + case 0x168: device->chip = &nv168_chipset; break; + case 0x172: device->chip = &nv172_chipset; break; + case 0x173: device->chip = &nv173_chipset; break; + case 0x174: device->chip = &nv174_chipset; break; + case 0x176: device->chip = &nv176_chipset; break; + case 0x177: device->chip = &nv177_chipset; break; + default: + if (nvkm_boolopt(device->cfgopt, "NvEnableUnsupportedChipsets", false)) { + switch (device->chipset) { + case 0x170: device->chip = &nv170_chipset; break; + default: + break; + } + } + + if (!device->chip) { + nvdev_error(device, "unknown chipset (%08x)\n", boot0); + ret = -ENODEV; + goto done; + } + break; + } + + nvdev_info(device, "NVIDIA %s (%08x)\n", + device->chip->name, boot0); + + /* vGPU detection */ + boot1 = nvkm_rd32(device, 0x0000004); + if (device->card_type >= TU100 && (boot1 & 0x00030000)) { + nvdev_info(device, "vGPUs are not supported\n"); + ret = -ENODEV; + goto done; + } + + /* read strapping information */ + strap = nvkm_rd32(device, 0x101000); + + /* determine frequency of timing crystal */ + if ( device->card_type <= NV_10 || device->chipset < 0x17 || + (device->chipset >= 0x20 && device->chipset < 0x25)) + strap &= 0x00000040; + else + strap &= 0x00400040; + + switch (strap) { + case 0x00000000: device->crystal = 13500; break; + case 0x00000040: device->crystal = 14318; break; + case 0x00400000: device->crystal = 27000; break; + case 0x00400040: device->crystal = 25000; break; + } + } else { + device->chip = &null_chipset; + } + + if (!device->name) + device->name = device->chip->name; + + mutex_init(&device->mutex); + +#define NVKM_LAYOUT_ONCE(type,data,ptr) \ + if (device->chip->ptr.inst && (subdev_mask & (BIT_ULL(type)))) { \ + WARN_ON(device->chip->ptr.inst != 0x00000001); \ + ret = device->chip->ptr.ctor(device, (type), -1, &device->ptr); \ + subdev = nvkm_device_subdev(device, (type), 0); \ + if (ret) { \ + nvkm_subdev_del(&subdev); \ + device->ptr = NULL; \ + if (ret != -ENODEV) { \ + nvdev_error(device, "%s ctor failed: %d\n", \ + nvkm_subdev_type[(type)], ret); \ + goto done; \ + } \ + } else { \ + subdev->pself = (void **)&device->ptr; \ + } \ + } +#define NVKM_LAYOUT_INST(type,data,ptr,cnt) \ + WARN_ON(device->chip->ptr.inst & ~((1 << ARRAY_SIZE(device->ptr)) - 1)); \ + for (j = 0; device->chip->ptr.inst && j < ARRAY_SIZE(device->ptr); j++) { \ + if ((device->chip->ptr.inst & BIT(j)) && (subdev_mask & BIT_ULL(type))) { \ + ret = device->chip->ptr.ctor(device, (type), (j), &device->ptr[j]); \ + subdev = nvkm_device_subdev(device, (type), (j)); \ + if (ret) { \ + nvkm_subdev_del(&subdev); \ + device->ptr[j] = NULL; \ + if (ret != -ENODEV) { \ + nvdev_error(device, "%s%d ctor failed: %d\n", \ + nvkm_subdev_type[(type)], (j), ret); \ + goto done; \ + } \ + } else { \ + subdev->pself = (void **)&device->ptr[j]; \ + } \ + } \ + } +#include +#undef NVKM_LAYOUT_INST +#undef NVKM_LAYOUT_ONCE + + ret = 0; +done: + if (device->pri && (!mmio || ret)) { + iounmap(device->pri); + device->pri = NULL; + } + mutex_unlock(&nv_devices_mutex); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c new file mode 100644 index 000000000..ce774579c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c @@ -0,0 +1,212 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "ctrl.h" + +#include +#include + +#include +#include +#include +#include + +static int +nvkm_control_mthd_pstate_info(struct nvkm_control *ctrl, void *data, u32 size) +{ + union { + struct nvif_control_pstate_info_v0 v0; + } *args = data; + struct nvkm_clk *clk = ctrl->device->clk; + int ret = -ENOSYS; + + nvif_ioctl(&ctrl->object, "control pstate info size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(&ctrl->object, "control pstate info vers %d\n", + args->v0.version); + } else + return ret; + + if (clk) { + args->v0.count = clk->state_nr; + args->v0.ustate_ac = clk->ustate_ac; + args->v0.ustate_dc = clk->ustate_dc; + args->v0.pwrsrc = clk->pwrsrc; + args->v0.pstate = clk->pstate; + } else { + args->v0.count = 0; + args->v0.ustate_ac = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE; + args->v0.ustate_dc = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE; + args->v0.pwrsrc = -ENODEV; + args->v0.pstate = NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN; + } + + return 0; +} + +static int +nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) +{ + union { + struct nvif_control_pstate_attr_v0 v0; + } *args = data; + struct nvkm_clk *clk = ctrl->device->clk; + const struct nvkm_domain *domain; + struct nvkm_pstate *pstate; + struct nvkm_cstate *cstate; + int i = 0, j = -1; + u32 lo, hi; + int ret = -ENOSYS; + + nvif_ioctl(&ctrl->object, "control pstate attr size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(&ctrl->object, + "control pstate attr vers %d state %d index %d\n", + args->v0.version, args->v0.state, args->v0.index); + if (!clk) + return -ENODEV; + if (args->v0.state < NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) + return -EINVAL; + if (args->v0.state >= clk->state_nr) + return -EINVAL; + } else + return ret; + domain = clk->domains; + + while (domain->name != nv_clk_src_max) { + if (domain->mname && ++j == args->v0.index) + break; + domain++; + } + + if (domain->name == nv_clk_src_max) + return -EINVAL; + + if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) { + list_for_each_entry(pstate, &clk->states, head) { + if (i++ == args->v0.state) + break; + } + + lo = pstate->base.domain[domain->name]; + hi = lo; + list_for_each_entry(cstate, &pstate->list, head) { + lo = min(lo, cstate->domain[domain->name]); + hi = max(hi, cstate->domain[domain->name]); + } + + args->v0.state = pstate->pstate; + } else { + lo = max(nvkm_clk_read(clk, domain->name), 0); + hi = lo; + } + + snprintf(args->v0.name, sizeof(args->v0.name), "%s", domain->mname); + snprintf(args->v0.unit, sizeof(args->v0.unit), "MHz"); + args->v0.min = lo / domain->mdiv; + args->v0.max = hi / domain->mdiv; + + args->v0.index = 0; + while ((++domain)->name != nv_clk_src_max) { + if (domain->mname) { + args->v0.index = ++j; + break; + } + } + + return 0; +} + +static int +nvkm_control_mthd_pstate_user(struct nvkm_control *ctrl, void *data, u32 size) +{ + union { + struct nvif_control_pstate_user_v0 v0; + } *args = data; + struct nvkm_clk *clk = ctrl->device->clk; + int ret = -ENOSYS; + + nvif_ioctl(&ctrl->object, "control pstate user size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(&ctrl->object, + "control pstate user vers %d ustate %d pwrsrc %d\n", + args->v0.version, args->v0.ustate, args->v0.pwrsrc); + if (!clk) + return -ENODEV; + } else + return ret; + + if (args->v0.pwrsrc >= 0) { + ret |= nvkm_clk_ustate(clk, args->v0.ustate, args->v0.pwrsrc); + } else { + ret |= nvkm_clk_ustate(clk, args->v0.ustate, 0); + ret |= nvkm_clk_ustate(clk, args->v0.ustate, 1); + } + + return ret; +} + +static int +nvkm_control_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_control *ctrl = nvkm_control(object); + switch (mthd) { + case NVIF_CONTROL_PSTATE_INFO: + return nvkm_control_mthd_pstate_info(ctrl, data, size); + case NVIF_CONTROL_PSTATE_ATTR: + return nvkm_control_mthd_pstate_attr(ctrl, data, size); + case NVIF_CONTROL_PSTATE_USER: + return nvkm_control_mthd_pstate_user(ctrl, data, size); + default: + break; + } + return -EINVAL; +} + +static const struct nvkm_object_func +nvkm_control = { + .mthd = nvkm_control_mthd, +}; + +static int +nvkm_control_new(struct nvkm_device *device, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_control *ctrl; + + if (!(ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL))) + return -ENOMEM; + *pobject = &ctrl->object; + ctrl->device = device; + + nvkm_object_ctor(&nvkm_control, oclass, &ctrl->object); + return 0; +} + +const struct nvkm_device_oclass +nvkm_control_oclass = { + .base.oclass = NVIF_CLASS_CONTROL, + .base.minver = -1, + .base.maxver = -1, + .ctor = nvkm_control_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.h b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.h new file mode 100644 index 000000000..9f6d7f23a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DEVICE_CTRL_H__ +#define __NVKM_DEVICE_CTRL_H__ +#define nvkm_control(p) container_of((p), struct nvkm_control, object) +#include + +struct nvkm_control { + struct nvkm_object object; + struct nvkm_device *device; +}; + +extern const struct nvkm_device_oclass nvkm_control_oclass; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c new file mode 100644 index 000000000..f302d2b57 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c @@ -0,0 +1,1695 @@ +/* + * Copyright 2015 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 +#include "priv.h" + +struct nvkm_device_pci_device { + u16 device; + const char *name; + const struct nvkm_device_pci_vendor *vendor; +}; + +struct nvkm_device_pci_vendor { + u16 vendor; + u16 device; + const char *name; + const struct nvkm_device_quirk quirk; +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0189[] = { + /* Apple iMac G4 NV18 */ + { 0x10de, 0x0010, NULL, { .tv_gpio = 4 } }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_01f0[] = { + /* MSI nForce2 IGP */ + { 0x1462, 0x5710, NULL, { .tv_pin_mask = 0xc } }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0322[] = { + /* Zotac FX5200 */ + { 0x19da, 0x1035, NULL, { .tv_pin_mask = 0xc } }, + { 0x19da, 0x2035, NULL, { .tv_pin_mask = 0xc } }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_05e7[] = { + { 0x10de, 0x0595, "Tesla T10 Processor" }, + { 0x10de, 0x068f, "Tesla T10 Processor" }, + { 0x10de, 0x0697, "Tesla M1060" }, + { 0x10de, 0x0714, "Tesla M1060" }, + { 0x10de, 0x0743, "Tesla M1060" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0609[] = { + { 0x106b, 0x00a7, "GeForce 8800 GS" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_062e[] = { + { 0x106b, 0x0605, "GeForce GT 130" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0649[] = { + { 0x1043, 0x202d, "GeForce GT 220M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0652[] = { + { 0x152d, 0x0850, "GeForce GT 240M LE" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0654[] = { + { 0x1043, 0x14a2, "GeForce GT 320M" }, + { 0x1043, 0x14d2, "GeForce GT 320M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0655[] = { + { 0x106b, 0x0633, "GeForce GT 120" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0656[] = { + { 0x106b, 0x0693, "GeForce GT 120" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_06d1[] = { + { 0x10de, 0x0771, "Tesla C2050" }, + { 0x10de, 0x0772, "Tesla C2070" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_06d2[] = { + { 0x10de, 0x088f, "Tesla X2070" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_06de[] = { + { 0x10de, 0x0773, "Tesla S2050" }, + { 0x10de, 0x082f, "Tesla M2050" }, + { 0x10de, 0x0840, "Tesla X2070" }, + { 0x10de, 0x0842, "Tesla M2050" }, + { 0x10de, 0x0846, "Tesla M2050" }, + { 0x10de, 0x0866, "Tesla M2050" }, + { 0x10de, 0x0907, "Tesla M2050" }, + { 0x10de, 0x091e, "Tesla M2050" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_06e8[] = { + { 0x103c, 0x360b, "GeForce 9200M GE" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_06f9[] = { + { 0x10de, 0x060d, "Quadro FX 370 Low Profile" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_06ff[] = { + { 0x10de, 0x0711, "HICx8 + Graphics" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0866[] = { + { 0x106b, 0x00b1, "GeForce 9400M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0872[] = { + { 0x1043, 0x1c42, "GeForce G205M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0873[] = { + { 0x1043, 0x1c52, "GeForce G205M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0a6e[] = { + { 0x17aa, 0x3607, "Second Generation ION" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0a70[] = { + { 0x17aa, 0x3605, "Second Generation ION" }, + { 0x17aa, 0x3617, "Second Generation ION" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0a73[] = { + { 0x17aa, 0x3607, "Second Generation ION" }, + { 0x17aa, 0x3610, "Second Generation ION" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0a74[] = { + { 0x17aa, 0x903a, "GeForce G210" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0a75[] = { + { 0x17aa, 0x3605, "Second Generation ION" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0a7a[] = { + { 0x1462, 0xaa51, "GeForce 405" }, + { 0x1462, 0xaa58, "GeForce 405" }, + { 0x1462, 0xac71, "GeForce 405" }, + { 0x1462, 0xac82, "GeForce 405" }, + { 0x1642, 0x3980, "GeForce 405" }, + { 0x17aa, 0x3950, "GeForce 405M" }, + { 0x17aa, 0x397d, "GeForce 405M" }, + { 0x1b0a, 0x90b4, "GeForce 405" }, + { 0x1bfd, 0x0003, "GeForce 405" }, + { 0x1bfd, 0x8006, "GeForce 405" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0dd8[] = { + { 0x10de, 0x0914, "Quadro 2000D" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0de9[] = { + { 0x1025, 0x0692, "GeForce GT 620M" }, + { 0x1025, 0x0725, "GeForce GT 620M" }, + { 0x1025, 0x0728, "GeForce GT 620M" }, + { 0x1025, 0x072b, "GeForce GT 620M" }, + { 0x1025, 0x072e, "GeForce GT 620M" }, + { 0x1025, 0x0753, "GeForce GT 620M" }, + { 0x1025, 0x0754, "GeForce GT 620M" }, + { 0x17aa, 0x3977, "GeForce GT 640M LE" }, + { 0x1b0a, 0x2210, "GeForce GT 635M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0dea[] = { + { 0x17aa, 0x365a, "GeForce 615" }, + { 0x17aa, 0x365b, "GeForce 615" }, + { 0x17aa, 0x365e, "GeForce 615" }, + { 0x17aa, 0x3660, "GeForce 615" }, + { 0x17aa, 0x366c, "GeForce 615" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0df4[] = { + { 0x152d, 0x0952, "GeForce GT 630M" }, + { 0x152d, 0x0953, "GeForce GT 630M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0fd2[] = { + { 0x1028, 0x0595, "GeForce GT 640M LE" }, + { 0x1028, 0x05b2, "GeForce GT 640M LE" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_0fe3[] = { + { 0x103c, 0x2b16, "GeForce GT 745A" }, + { 0x17aa, 0x3675, "GeForce GT 745A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_104b[] = { + { 0x1043, 0x844c, "GeForce GT 625" }, + { 0x1043, 0x846b, "GeForce GT 625" }, + { 0x1462, 0xb590, "GeForce GT 625" }, + { 0x174b, 0x0625, "GeForce GT 625" }, + { 0x174b, 0xa625, "GeForce GT 625" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1058[] = { + { 0x103c, 0x2af1, "GeForce 610" }, + { 0x17aa, 0x3682, "GeForce 800A" }, + { 0x17aa, 0x3692, "GeForce 705A" }, + { 0x17aa, 0x3695, "GeForce 800A" }, + { 0x17aa, 0x36a8, "GeForce 800A" }, + { 0x17aa, 0x36ac, "GeForce 800A" }, + { 0x17aa, 0x36ad, "GeForce 800A" }, + { 0x705a, 0x3682, "GeForce 800A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_105b[] = { + { 0x103c, 0x2afb, "GeForce 705A" }, + { 0x17aa, 0x36a1, "GeForce 800A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1091[] = { + { 0x10de, 0x088e, "Tesla X2090" }, + { 0x10de, 0x0891, "Tesla X2090" }, + { 0x10de, 0x0974, "Tesla X2090" }, + { 0x10de, 0x098d, "Tesla X2090" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1096[] = { + { 0x10de, 0x0911, "Tesla C2050" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1140[] = { + { 0x1019, 0x999f, "GeForce GT 720M" }, + { 0x1025, 0x0600, "GeForce GT 620M" }, + { 0x1025, 0x0606, "GeForce GT 620M" }, + { 0x1025, 0x064a, "GeForce GT 620M" }, + { 0x1025, 0x064c, "GeForce GT 620M" }, + { 0x1025, 0x067a, "GeForce GT 620M" }, + { 0x1025, 0x0680, "GeForce GT 620M" }, + { 0x1025, 0x0686, "GeForce 710M" }, + { 0x1025, 0x0689, "GeForce 710M" }, + { 0x1025, 0x068b, "GeForce 710M" }, + { 0x1025, 0x068d, "GeForce 710M" }, + { 0x1025, 0x068e, "GeForce 710M" }, + { 0x1025, 0x0691, "GeForce 710M" }, + { 0x1025, 0x0692, "GeForce GT 620M" }, + { 0x1025, 0x0694, "GeForce GT 620M" }, + { 0x1025, 0x0702, "GeForce GT 620M" }, + { 0x1025, 0x0719, "GeForce GT 620M" }, + { 0x1025, 0x0725, "GeForce GT 620M" }, + { 0x1025, 0x0728, "GeForce GT 620M" }, + { 0x1025, 0x072b, "GeForce GT 620M" }, + { 0x1025, 0x072e, "GeForce GT 620M" }, + { 0x1025, 0x0732, "GeForce GT 620M" }, + { 0x1025, 0x0763, "GeForce GT 720M" }, + { 0x1025, 0x0773, "GeForce 710M" }, + { 0x1025, 0x0774, "GeForce 710M" }, + { 0x1025, 0x0776, "GeForce GT 720M" }, + { 0x1025, 0x077a, "GeForce 710M" }, + { 0x1025, 0x077b, "GeForce 710M" }, + { 0x1025, 0x077c, "GeForce 710M" }, + { 0x1025, 0x077d, "GeForce 710M" }, + { 0x1025, 0x077e, "GeForce 710M" }, + { 0x1025, 0x077f, "GeForce 710M" }, + { 0x1025, 0x0781, "GeForce GT 720M" }, + { 0x1025, 0x0798, "GeForce GT 720M" }, + { 0x1025, 0x0799, "GeForce GT 720M" }, + { 0x1025, 0x079b, "GeForce GT 720M" }, + { 0x1025, 0x079c, "GeForce GT 720M" }, + { 0x1025, 0x0807, "GeForce GT 720M" }, + { 0x1025, 0x0821, "GeForce 820M" }, + { 0x1025, 0x0823, "GeForce GT 720M" }, + { 0x1025, 0x0830, "GeForce GT 720M" }, + { 0x1025, 0x0833, "GeForce GT 720M" }, + { 0x1025, 0x0837, "GeForce GT 720M" }, + { 0x1025, 0x083e, "GeForce 820M" }, + { 0x1025, 0x0841, "GeForce 710M" }, + { 0x1025, 0x0853, "GeForce 820M" }, + { 0x1025, 0x0854, "GeForce 820M" }, + { 0x1025, 0x0855, "GeForce 820M" }, + { 0x1025, 0x0856, "GeForce 820M" }, + { 0x1025, 0x0857, "GeForce 820M" }, + { 0x1025, 0x0858, "GeForce 820M" }, + { 0x1025, 0x0863, "GeForce 820M" }, + { 0x1025, 0x0868, "GeForce 820M" }, + { 0x1025, 0x0869, "GeForce 810M" }, + { 0x1025, 0x0873, "GeForce 820M" }, + { 0x1025, 0x0878, "GeForce 820M" }, + { 0x1025, 0x087b, "GeForce 820M" }, + { 0x1025, 0x087f, "GeForce 820M" }, + { 0x1025, 0x0881, "GeForce 820M" }, + { 0x1025, 0x0885, "GeForce 820M" }, + { 0x1025, 0x088a, "GeForce 820M" }, + { 0x1025, 0x089b, "GeForce 820M" }, + { 0x1025, 0x0921, "GeForce 820M" }, + { 0x1025, 0x092e, "GeForce 810M" }, + { 0x1025, 0x092f, "GeForce 820M" }, + { 0x1025, 0x0932, "GeForce 820M" }, + { 0x1025, 0x093a, "GeForce 820M" }, + { 0x1025, 0x093c, "GeForce 820M" }, + { 0x1025, 0x093f, "GeForce 820M" }, + { 0x1025, 0x0941, "GeForce 820M" }, + { 0x1025, 0x0945, "GeForce 820M" }, + { 0x1025, 0x0954, "GeForce 820M" }, + { 0x1025, 0x0965, "GeForce 820M" }, + { 0x1028, 0x054d, "GeForce GT 630M" }, + { 0x1028, 0x054e, "GeForce GT 630M" }, + { 0x1028, 0x0554, "GeForce GT 620M" }, + { 0x1028, 0x0557, "GeForce GT 620M" }, + { 0x1028, 0x0562, "GeForce GT625M" }, + { 0x1028, 0x0565, "GeForce GT 630M" }, + { 0x1028, 0x0568, "GeForce GT 630M" }, + { 0x1028, 0x0590, "GeForce GT 630M" }, + { 0x1028, 0x0592, "GeForce GT625M" }, + { 0x1028, 0x0594, "GeForce GT625M" }, + { 0x1028, 0x0595, "GeForce GT625M" }, + { 0x1028, 0x05a2, "GeForce GT625M" }, + { 0x1028, 0x05b1, "GeForce GT625M" }, + { 0x1028, 0x05b3, "GeForce GT625M" }, + { 0x1028, 0x05da, "GeForce GT 630M" }, + { 0x1028, 0x05de, "GeForce GT 720M" }, + { 0x1028, 0x05e0, "GeForce GT 720M" }, + { 0x1028, 0x05e8, "GeForce GT 630M" }, + { 0x1028, 0x05f4, "GeForce GT 720M" }, + { 0x1028, 0x060f, "GeForce GT 720M" }, + { 0x1028, 0x062f, "GeForce GT 720M" }, + { 0x1028, 0x064e, "GeForce 820M" }, + { 0x1028, 0x0652, "GeForce 820M" }, + { 0x1028, 0x0653, "GeForce 820M" }, + { 0x1028, 0x0655, "GeForce 820M" }, + { 0x1028, 0x065e, "GeForce 820M" }, + { 0x1028, 0x0662, "GeForce 820M" }, + { 0x1028, 0x068d, "GeForce 820M" }, + { 0x1028, 0x06ad, "GeForce 820M" }, + { 0x1028, 0x06ae, "GeForce 820M" }, + { 0x1028, 0x06af, "GeForce 820M" }, + { 0x1028, 0x06b0, "GeForce 820M" }, + { 0x1028, 0x06c0, "GeForce 820M" }, + { 0x1028, 0x06c1, "GeForce 820M" }, + { 0x103c, 0x18ef, "GeForce GT 630M" }, + { 0x103c, 0x18f9, "GeForce GT 630M" }, + { 0x103c, 0x18fb, "GeForce GT 630M" }, + { 0x103c, 0x18fd, "GeForce GT 630M" }, + { 0x103c, 0x18ff, "GeForce GT 630M" }, + { 0x103c, 0x218a, "GeForce 820M" }, + { 0x103c, 0x21bb, "GeForce 820M" }, + { 0x103c, 0x21bc, "GeForce 820M" }, + { 0x103c, 0x220e, "GeForce 820M" }, + { 0x103c, 0x2210, "GeForce 820M" }, + { 0x103c, 0x2212, "GeForce 820M" }, + { 0x103c, 0x2214, "GeForce 820M" }, + { 0x103c, 0x2218, "GeForce 820M" }, + { 0x103c, 0x225b, "GeForce 820M" }, + { 0x103c, 0x225d, "GeForce 820M" }, + { 0x103c, 0x226d, "GeForce 820M" }, + { 0x103c, 0x226f, "GeForce 820M" }, + { 0x103c, 0x22d2, "GeForce 820M" }, + { 0x103c, 0x22d9, "GeForce 820M" }, + { 0x103c, 0x2335, "GeForce 820M" }, + { 0x103c, 0x2337, "GeForce 820M" }, + { 0x103c, 0x2aef, "GeForce GT 720A" }, + { 0x103c, 0x2af9, "GeForce 710A" }, + { 0x1043, 0x10dd, "NVS 5200M" }, + { 0x1043, 0x10ed, "NVS 5200M" }, + { 0x1043, 0x11fd, "GeForce GT 720M" }, + { 0x1043, 0x124d, "GeForce GT 720M" }, + { 0x1043, 0x126d, "GeForce GT 720M" }, + { 0x1043, 0x131d, "GeForce GT 720M" }, + { 0x1043, 0x13fd, "GeForce GT 720M" }, + { 0x1043, 0x14c7, "GeForce GT 720M" }, + { 0x1043, 0x1507, "GeForce GT 620M" }, + { 0x1043, 0x15ad, "GeForce 820M" }, + { 0x1043, 0x15ed, "GeForce 820M" }, + { 0x1043, 0x160d, "GeForce 820M" }, + { 0x1043, 0x163d, "GeForce 820M" }, + { 0x1043, 0x165d, "GeForce 820M" }, + { 0x1043, 0x166d, "GeForce 820M" }, + { 0x1043, 0x16cd, "GeForce 820M" }, + { 0x1043, 0x16dd, "GeForce 820M" }, + { 0x1043, 0x170d, "GeForce 820M" }, + { 0x1043, 0x176d, "GeForce 820M" }, + { 0x1043, 0x178d, "GeForce 820M" }, + { 0x1043, 0x179d, "GeForce 820M" }, + { 0x1043, 0x2132, "GeForce GT 620M" }, + { 0x1043, 0x2136, "NVS 5200M" }, + { 0x1043, 0x21ba, "GeForce GT 720M" }, + { 0x1043, 0x21fa, "GeForce GT 720M" }, + { 0x1043, 0x220a, "GeForce GT 720M" }, + { 0x1043, 0x221a, "GeForce GT 720M" }, + { 0x1043, 0x223a, "GeForce GT 710M" }, + { 0x1043, 0x224a, "GeForce GT 710M" }, + { 0x1043, 0x227a, "GeForce 820M" }, + { 0x1043, 0x228a, "GeForce 820M" }, + { 0x1043, 0x22fa, "GeForce 820M" }, + { 0x1043, 0x232a, "GeForce 820M" }, + { 0x1043, 0x233a, "GeForce 820M" }, + { 0x1043, 0x235a, "GeForce 820M" }, + { 0x1043, 0x236a, "GeForce 820M" }, + { 0x1043, 0x238a, "GeForce 820M" }, + { 0x1043, 0x8595, "GeForce GT 720M" }, + { 0x1043, 0x85ea, "GeForce GT 720M" }, + { 0x1043, 0x85eb, "GeForce 820M" }, + { 0x1043, 0x85ec, "GeForce 820M" }, + { 0x1043, 0x85ee, "GeForce GT 720M" }, + { 0x1043, 0x85f3, "GeForce 820M" }, + { 0x1043, 0x860e, "GeForce 820M" }, + { 0x1043, 0x861a, "GeForce 820M" }, + { 0x1043, 0x861b, "GeForce 820M" }, + { 0x1043, 0x8628, "GeForce 820M" }, + { 0x1043, 0x8643, "GeForce 820M" }, + { 0x1043, 0x864c, "GeForce 820M" }, + { 0x1043, 0x8652, "GeForce 820M" }, + { 0x1043, 0x8660, "GeForce 820M" }, + { 0x1043, 0x8661, "GeForce 820M" }, + { 0x105b, 0x0dac, "GeForce GT 720M" }, + { 0x105b, 0x0dad, "GeForce GT 720M" }, + { 0x105b, 0x0ef3, "GeForce GT 720M" }, + { 0x10cf, 0x17f5, "GeForce GT 720M" }, + { 0x1179, 0xfa01, "GeForce 710M" }, + { 0x1179, 0xfa02, "GeForce 710M" }, + { 0x1179, 0xfa03, "GeForce 710M" }, + { 0x1179, 0xfa05, "GeForce 710M" }, + { 0x1179, 0xfa11, "GeForce 710M" }, + { 0x1179, 0xfa13, "GeForce 710M" }, + { 0x1179, 0xfa18, "GeForce 710M" }, + { 0x1179, 0xfa19, "GeForce 710M" }, + { 0x1179, 0xfa21, "GeForce 710M" }, + { 0x1179, 0xfa23, "GeForce 710M" }, + { 0x1179, 0xfa2a, "GeForce 710M" }, + { 0x1179, 0xfa32, "GeForce 710M" }, + { 0x1179, 0xfa33, "GeForce 710M" }, + { 0x1179, 0xfa36, "GeForce 710M" }, + { 0x1179, 0xfa38, "GeForce 710M" }, + { 0x1179, 0xfa42, "GeForce 710M" }, + { 0x1179, 0xfa43, "GeForce 710M" }, + { 0x1179, 0xfa45, "GeForce 710M" }, + { 0x1179, 0xfa47, "GeForce 710M" }, + { 0x1179, 0xfa49, "GeForce 710M" }, + { 0x1179, 0xfa58, "GeForce 710M" }, + { 0x1179, 0xfa59, "GeForce 710M" }, + { 0x1179, 0xfa88, "GeForce 710M" }, + { 0x1179, 0xfa89, "GeForce 710M" }, + { 0x144d, 0xb092, "GeForce GT 620M" }, + { 0x144d, 0xc0d5, "GeForce GT 630M" }, + { 0x144d, 0xc0d7, "GeForce GT 620M" }, + { 0x144d, 0xc0e2, "NVS 5200M" }, + { 0x144d, 0xc0e3, "NVS 5200M" }, + { 0x144d, 0xc0e4, "NVS 5200M" }, + { 0x144d, 0xc10d, "GeForce 820M" }, + { 0x144d, 0xc652, "GeForce GT 620M" }, + { 0x144d, 0xc709, "GeForce 710M" }, + { 0x144d, 0xc711, "GeForce 710M" }, + { 0x144d, 0xc736, "GeForce 710M" }, + { 0x144d, 0xc737, "GeForce 710M" }, + { 0x144d, 0xc745, "GeForce 820M" }, + { 0x144d, 0xc750, "GeForce 820M" }, + { 0x1462, 0x10b8, "GeForce GT 710M" }, + { 0x1462, 0x10e9, "GeForce GT 720M" }, + { 0x1462, 0x1116, "GeForce 820M" }, + { 0x1462, 0xaa33, "GeForce 720M" }, + { 0x1462, 0xaaa2, "GeForce GT 720M" }, + { 0x1462, 0xaaa3, "GeForce 820M" }, + { 0x1462, 0xacb2, "GeForce GT 720M" }, + { 0x1462, 0xacc1, "GeForce GT 720M" }, + { 0x1462, 0xae61, "GeForce 720M" }, + { 0x1462, 0xae65, "GeForce GT 720M" }, + { 0x1462, 0xae6a, "GeForce 820M" }, + { 0x1462, 0xae71, "GeForce GT 720M" }, + { 0x14c0, 0x0083, "GeForce 820M" }, + { 0x152d, 0x0926, "GeForce 620M" }, + { 0x152d, 0x0982, "GeForce GT 630M" }, + { 0x152d, 0x0983, "GeForce GT 630M" }, + { 0x152d, 0x1005, "GeForce GT820M" }, + { 0x152d, 0x1012, "GeForce 710M" }, + { 0x152d, 0x1019, "GeForce 820M" }, + { 0x152d, 0x1030, "GeForce GT 630M" }, + { 0x152d, 0x1055, "GeForce 710M" }, + { 0x152d, 0x1067, "GeForce GT 720M" }, + { 0x152d, 0x1092, "GeForce 820M" }, + { 0x17aa, 0x2200, "NVS 5200M" }, + { 0x17aa, 0x2213, "GeForce GT 720M" }, + { 0x17aa, 0x2220, "GeForce GT 720M" }, + { 0x17aa, 0x309c, "GeForce GT 720A" }, + { 0x17aa, 0x30b4, "GeForce 820A" }, + { 0x17aa, 0x30b7, "GeForce 720A" }, + { 0x17aa, 0x30e4, "GeForce 820A" }, + { 0x17aa, 0x361b, "GeForce 820A" }, + { 0x17aa, 0x361c, "GeForce 820A" }, + { 0x17aa, 0x361d, "GeForce 820A" }, + { 0x17aa, 0x3656, "GeForce GT620M" }, + { 0x17aa, 0x365a, "GeForce 705M" }, + { 0x17aa, 0x365e, "GeForce 800M" }, + { 0x17aa, 0x3661, "GeForce 820A" }, + { 0x17aa, 0x366c, "GeForce 800M" }, + { 0x17aa, 0x3685, "GeForce 800M" }, + { 0x17aa, 0x3686, "GeForce 800M" }, + { 0x17aa, 0x3687, "GeForce 705A" }, + { 0x17aa, 0x3696, "GeForce 820A" }, + { 0x17aa, 0x369b, "GeForce 820A" }, + { 0x17aa, 0x369c, "GeForce 820A" }, + { 0x17aa, 0x369d, "GeForce 820A" }, + { 0x17aa, 0x369e, "GeForce 820A" }, + { 0x17aa, 0x36a6, "GeForce 820A" }, + { 0x17aa, 0x36a7, "GeForce 820A" }, + { 0x17aa, 0x36a9, "GeForce 820A" }, + { 0x17aa, 0x36af, "GeForce 820A" }, + { 0x17aa, 0x36b0, "GeForce 820A" }, + { 0x17aa, 0x36b6, "GeForce 820A" }, + { 0x17aa, 0x3800, "GeForce GT 720M" }, + { 0x17aa, 0x3801, "GeForce GT 720M" }, + { 0x17aa, 0x3802, "GeForce GT 720M" }, + { 0x17aa, 0x3803, "GeForce GT 720M" }, + { 0x17aa, 0x3804, "GeForce GT 720M" }, + { 0x17aa, 0x3806, "GeForce GT 720M" }, + { 0x17aa, 0x3808, "GeForce GT 720M" }, + { 0x17aa, 0x380d, "GeForce 820M" }, + { 0x17aa, 0x380e, "GeForce 820M" }, + { 0x17aa, 0x380f, "GeForce 820M" }, + { 0x17aa, 0x3811, "GeForce 820M" }, + { 0x17aa, 0x3812, "GeForce 820M" }, + { 0x17aa, 0x3813, "GeForce 820M" }, + { 0x17aa, 0x3816, "GeForce 820M" }, + { 0x17aa, 0x3817, "GeForce 820M" }, + { 0x17aa, 0x3818, "GeForce 820M" }, + { 0x17aa, 0x381a, "GeForce 820M" }, + { 0x17aa, 0x381c, "GeForce 820M" }, + { 0x17aa, 0x381d, "GeForce 820M" }, + { 0x17aa, 0x3901, "GeForce 610M" }, + { 0x17aa, 0x3902, "GeForce 710M" }, + { 0x17aa, 0x3903, "GeForce 710M" }, + { 0x17aa, 0x3904, "GeForce GT 625M" }, + { 0x17aa, 0x3905, "GeForce GT 720M" }, + { 0x17aa, 0x3907, "GeForce 820M" }, + { 0x17aa, 0x3910, "GeForce GT 720M" }, + { 0x17aa, 0x3912, "GeForce GT 720M" }, + { 0x17aa, 0x3913, "GeForce 820M" }, + { 0x17aa, 0x3915, "GeForce 820M" }, + { 0x17aa, 0x3983, "GeForce 610M" }, + { 0x17aa, 0x5001, "GeForce 610M" }, + { 0x17aa, 0x5003, "GeForce GT 720M" }, + { 0x17aa, 0x5005, "GeForce 705M" }, + { 0x17aa, 0x500d, "GeForce GT 620M" }, + { 0x17aa, 0x5014, "GeForce 710M" }, + { 0x17aa, 0x5017, "GeForce 710M" }, + { 0x17aa, 0x5019, "GeForce 710M" }, + { 0x17aa, 0x501a, "GeForce 710M" }, + { 0x17aa, 0x501f, "GeForce GT 720M" }, + { 0x17aa, 0x5025, "GeForce 710M" }, + { 0x17aa, 0x5027, "GeForce 710M" }, + { 0x17aa, 0x502a, "GeForce 710M" }, + { 0x17aa, 0x502b, "GeForce GT 720M" }, + { 0x17aa, 0x502d, "GeForce 710M" }, + { 0x17aa, 0x502e, "GeForce GT 720M" }, + { 0x17aa, 0x502f, "GeForce GT 720M" }, + { 0x17aa, 0x5030, "GeForce 705M" }, + { 0x17aa, 0x5031, "GeForce 705M" }, + { 0x17aa, 0x5032, "GeForce 820M" }, + { 0x17aa, 0x5033, "GeForce 820M" }, + { 0x17aa, 0x503e, "GeForce 710M" }, + { 0x17aa, 0x503f, "GeForce 820M" }, + { 0x17aa, 0x5040, "GeForce 820M" }, + { 0x1854, 0x0177, "GeForce 710M" }, + { 0x1854, 0x0180, "GeForce 710M" }, + { 0x1854, 0x0190, "GeForce GT 720M" }, + { 0x1854, 0x0192, "GeForce GT 720M" }, + { 0x1854, 0x0224, "GeForce 820M" }, + { 0x1b0a, 0x20dd, "GeForce GT 620M" }, + { 0x1b0a, 0x20df, "GeForce GT 620M" }, + { 0x1b0a, 0x210e, "GeForce 820M" }, + { 0x1b0a, 0x2202, "GeForce GT 720M" }, + { 0x1b0a, 0x90d7, "GeForce 820M" }, + { 0x1b0a, 0x90dd, "GeForce 820M" }, + { 0x1b50, 0x5530, "GeForce 820M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1185[] = { + { 0x10de, 0x106f, "GeForce GTX 760" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1189[] = { + { 0x10de, 0x1074, "GeForce GTX 760 Ti OEM" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1199[] = { + { 0x1458, 0xd001, "GeForce GTX 760" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_11e3[] = { + { 0x17aa, 0x3683, "GeForce GTX 760A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1247[] = { + { 0x1043, 0x212a, "GeForce GT 635M" }, + { 0x1043, 0x212b, "GeForce GT 635M" }, + { 0x1043, 0x212c, "GeForce GT 635M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_124d[] = { + { 0x1462, 0x10cc, "GeForce GT 635M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1290[] = { + { 0x103c, 0x2afa, "GeForce 730A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1292[] = { + { 0x17aa, 0x3675, "GeForce GT 740A" }, + { 0x17aa, 0x367c, "GeForce GT 740A" }, + { 0x17aa, 0x3684, "GeForce GT 740A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1295[] = { + { 0x103c, 0x2b0d, "GeForce 710A" }, + { 0x103c, 0x2b0f, "GeForce 710A" }, + { 0x103c, 0x2b20, "GeForce 810A" }, + { 0x103c, 0x2b21, "GeForce 810A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1299[] = { + { 0x17aa, 0x369b, "GeForce 920A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1340[] = { + { 0x103c, 0x2b2b, "GeForce 830A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1341[] = { + { 0x17aa, 0x3697, "GeForce 840A" }, + { 0x17aa, 0x3699, "GeForce 840A" }, + { 0x17aa, 0x369c, "GeForce 840A" }, + { 0x17aa, 0x36af, "GeForce 840A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1346[] = { + { 0x17aa, 0x30ba, "GeForce 930A" }, + { 0x17aa, 0x362c, "GeForce 930A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1347[] = { + { 0x17aa, 0x36b9, "GeForce 940A" }, + { 0x17aa, 0x36ba, "GeForce 940A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_137a[] = { + { 0x17aa, 0x2225, "Quadro K620M" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_137d[] = { + { 0x17aa, 0x3699, "GeForce 940A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1391[] = { + { 0x17aa, 0x3697, "GeForce GTX 850A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_1392[] = { + { 0x1028, 0x066a, "GeForce GPU" }, + { 0x1043, 0x861e, "GeForce GTX 750 Ti" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_139a[] = { + { 0x17aa, 0x36b9, "GeForce GTX 950A" }, + {} +}; + +static const struct nvkm_device_pci_vendor +nvkm_device_pci_10de_139b[] = { + { 0x1028, 0x06a3, "GeForce GTX 860M" }, + { 0x19da, 0xc248, "GeForce GTX 750 Ti" }, + {} +}; + +static const struct nvkm_device_pci_device +nvkm_device_pci_10de[] = { + { 0x0020, "RIVA TNT" }, + { 0x0028, "RIVA TNT2/TNT2 Pro" }, + { 0x0029, "RIVA TNT2 Ultra" }, + { 0x002c, "Vanta/Vanta LT" }, + { 0x002d, "RIVA TNT2 Model 64/Model 64 Pro" }, + { 0x0040, "GeForce 6800 Ultra" }, + { 0x0041, "GeForce 6800" }, + { 0x0042, "GeForce 6800 LE" }, + { 0x0043, "GeForce 6800 XE" }, + { 0x0044, "GeForce 6800 XT" }, + { 0x0045, "GeForce 6800 GT" }, + { 0x0046, "GeForce 6800 GT" }, + { 0x0047, "GeForce 6800 GS" }, + { 0x0048, "GeForce 6800 XT" }, + { 0x004e, "Quadro FX 4000" }, + { 0x0090, "GeForce 7800 GTX" }, + { 0x0091, "GeForce 7800 GTX" }, + { 0x0092, "GeForce 7800 GT" }, + { 0x0093, "GeForce 7800 GS" }, + { 0x0095, "GeForce 7800 SLI" }, + { 0x0098, "GeForce Go 7800" }, + { 0x0099, "GeForce Go 7800 GTX" }, + { 0x009d, "Quadro FX 4500" }, + { 0x00a0, "Aladdin TNT2" }, + { 0x00c0, "GeForce 6800 GS" }, + { 0x00c1, "GeForce 6800" }, + { 0x00c2, "GeForce 6800 LE" }, + { 0x00c3, "GeForce 6800 XT" }, + { 0x00c8, "GeForce Go 6800" }, + { 0x00c9, "GeForce Go 6800 Ultra" }, + { 0x00cc, "Quadro FX Go1400" }, + { 0x00cd, "Quadro FX 3450/4000 SDI" }, + { 0x00ce, "Quadro FX 1400" }, + { 0x00f1, "GeForce 6600 GT" }, + { 0x00f2, "GeForce 6600" }, + { 0x00f3, "GeForce 6200" }, + { 0x00f4, "GeForce 6600 LE" }, + { 0x00f5, "GeForce 7800 GS" }, + { 0x00f6, "GeForce 6800 GS" }, + { 0x00f8, "Quadro FX 3400/Quadro FX 4000" }, + { 0x00f9, "GeForce 6800 Ultra" }, + { 0x00fa, "GeForce PCX 5750" }, + { 0x00fb, "GeForce PCX 5900" }, + { 0x00fc, "Quadro FX 330/GeForce PCX 5300" }, + { 0x00fd, "Quadro FX 330/Quadro NVS 280 PCI-E" }, + { 0x00fe, "Quadro FX 1300" }, + { 0x0100, "GeForce 256" }, + { 0x0101, "GeForce DDR" }, + { 0x0103, "Quadro" }, + { 0x0110, "GeForce2 MX/MX 400" }, + { 0x0111, "GeForce2 MX 100/200" }, + { 0x0112, "GeForce2 Go" }, + { 0x0113, "Quadro2 MXR/EX/Go" }, + { 0x0140, "GeForce 6600 GT" }, + { 0x0141, "GeForce 6600" }, + { 0x0142, "GeForce 6600 LE" }, + { 0x0143, "GeForce 6600 VE" }, + { 0x0144, "GeForce Go 6600" }, + { 0x0145, "GeForce 6610 XL" }, + { 0x0146, "GeForce Go 6600 TE/6200 TE" }, + { 0x0147, "GeForce 6700 XL" }, + { 0x0148, "GeForce Go 6600" }, + { 0x0149, "GeForce Go 6600 GT" }, + { 0x014a, "Quadro NVS 440" }, + { 0x014c, "Quadro FX 540M" }, + { 0x014d, "Quadro FX 550" }, + { 0x014e, "Quadro FX 540" }, + { 0x014f, "GeForce 6200" }, + { 0x0150, "GeForce2 GTS/GeForce2 Pro" }, + { 0x0151, "GeForce2 Ti" }, + { 0x0152, "GeForce2 Ultra" }, + { 0x0153, "Quadro2 Pro" }, + { 0x0160, "GeForce 6500" }, + { 0x0161, "GeForce 6200 TurboCache(TM)" }, + { 0x0162, "GeForce 6200SE TurboCache(TM)" }, + { 0x0163, "GeForce 6200 LE" }, + { 0x0164, "GeForce Go 6200" }, + { 0x0165, "Quadro NVS 285" }, + { 0x0166, "GeForce Go 6400" }, + { 0x0167, "GeForce Go 6200" }, + { 0x0168, "GeForce Go 6400" }, + { 0x0169, "GeForce 6250" }, + { 0x016a, "GeForce 7100 GS" }, + { 0x0170, "GeForce4 MX 460" }, + { 0x0171, "GeForce4 MX 440" }, + { 0x0172, "GeForce4 MX 420" }, + { 0x0173, "GeForce4 MX 440-SE" }, + { 0x0174, "GeForce4 440 Go" }, + { 0x0175, "GeForce4 420 Go" }, + { 0x0176, "GeForce4 420 Go 32M" }, + { 0x0177, "GeForce4 460 Go" }, + { 0x0178, "Quadro4 550 XGL" }, + { 0x0179, "GeForce4 440 Go 64M" }, + { 0x017a, "Quadro NVS 400" }, + { 0x017c, "Quadro4 500 GoGL" }, + { 0x017d, "GeForce4 410 Go 16M" }, + { 0x0181, "GeForce4 MX 440 with AGP8X" }, + { 0x0182, "GeForce4 MX 440SE with AGP8X" }, + { 0x0183, "GeForce4 MX 420 with AGP8X" }, + { 0x0185, "GeForce4 MX 4000" }, + { 0x0188, "Quadro4 580 XGL" }, + { 0x0189, "GeForce4 MX with AGP8X (Mac)", nvkm_device_pci_10de_0189 }, + { 0x018a, "Quadro NVS 280 SD" }, + { 0x018b, "Quadro4 380 XGL" }, + { 0x018c, "Quadro NVS 50 PCI" }, + { 0x0191, "GeForce 8800 GTX" }, + { 0x0193, "GeForce 8800 GTS" }, + { 0x0194, "GeForce 8800 Ultra" }, + { 0x0197, "Tesla C870" }, + { 0x019d, "Quadro FX 5600" }, + { 0x019e, "Quadro FX 4600" }, + { 0x01a0, "GeForce2 Integrated GPU" }, + { 0x01d0, "GeForce 7350 LE" }, + { 0x01d1, "GeForce 7300 LE" }, + { 0x01d2, "GeForce 7550 LE" }, + { 0x01d3, "GeForce 7300 SE/7200 GS" }, + { 0x01d6, "GeForce Go 7200" }, + { 0x01d7, "GeForce Go 7300" }, + { 0x01d8, "GeForce Go 7400" }, + { 0x01da, "Quadro NVS 110M" }, + { 0x01db, "Quadro NVS 120M" }, + { 0x01dc, "Quadro FX 350M" }, + { 0x01dd, "GeForce 7500 LE" }, + { 0x01de, "Quadro FX 350" }, + { 0x01df, "GeForce 7300 GS" }, + { 0x01f0, "GeForce4 MX Integrated GPU", nvkm_device_pci_10de_01f0 }, + { 0x0200, "GeForce3" }, + { 0x0201, "GeForce3 Ti 200" }, + { 0x0202, "GeForce3 Ti 500" }, + { 0x0203, "Quadro DCC" }, + { 0x0211, "GeForce 6800" }, + { 0x0212, "GeForce 6800 LE" }, + { 0x0215, "GeForce 6800 GT" }, + { 0x0218, "GeForce 6800 XT" }, + { 0x0221, "GeForce 6200" }, + { 0x0222, "GeForce 6200 A-LE" }, + { 0x0240, "GeForce 6150" }, + { 0x0241, "GeForce 6150 LE" }, + { 0x0242, "GeForce 6100" }, + { 0x0244, "GeForce Go 6150" }, + { 0x0245, "Quadro NVS 210S / GeForce 6150LE" }, + { 0x0247, "GeForce Go 6100" }, + { 0x0250, "GeForce4 Ti 4600" }, + { 0x0251, "GeForce4 Ti 4400" }, + { 0x0253, "GeForce4 Ti 4200" }, + { 0x0258, "Quadro4 900 XGL" }, + { 0x0259, "Quadro4 750 XGL" }, + { 0x025b, "Quadro4 700 XGL" }, + { 0x0280, "GeForce4 Ti 4800" }, + { 0x0281, "GeForce4 Ti 4200 with AGP8X" }, + { 0x0282, "GeForce4 Ti 4800 SE" }, + { 0x0286, "GeForce4 4200 Go" }, + { 0x0288, "Quadro4 980 XGL" }, + { 0x0289, "Quadro4 780 XGL" }, + { 0x028c, "Quadro4 700 GoGL" }, + { 0x0290, "GeForce 7900 GTX" }, + { 0x0291, "GeForce 7900 GT/GTO" }, + { 0x0292, "GeForce 7900 GS" }, + { 0x0293, "GeForce 7950 GX2" }, + { 0x0294, "GeForce 7950 GX2" }, + { 0x0295, "GeForce 7950 GT" }, + { 0x0297, "GeForce Go 7950 GTX" }, + { 0x0298, "GeForce Go 7900 GS" }, + { 0x0299, "Quadro NVS 510M" }, + { 0x029a, "Quadro FX 2500M" }, + { 0x029b, "Quadro FX 1500M" }, + { 0x029c, "Quadro FX 5500" }, + { 0x029d, "Quadro FX 3500" }, + { 0x029e, "Quadro FX 1500" }, + { 0x029f, "Quadro FX 4500 X2" }, + { 0x02e0, "GeForce 7600 GT" }, + { 0x02e1, "GeForce 7600 GS" }, + { 0x02e2, "GeForce 7300 GT" }, + { 0x02e3, "GeForce 7900 GS" }, + { 0x02e4, "GeForce 7950 GT" }, + { 0x0301, "GeForce FX 5800 Ultra" }, + { 0x0302, "GeForce FX 5800" }, + { 0x0308, "Quadro FX 2000" }, + { 0x0309, "Quadro FX 1000" }, + { 0x0311, "GeForce FX 5600 Ultra" }, + { 0x0312, "GeForce FX 5600" }, + { 0x0314, "GeForce FX 5600XT" }, + { 0x031a, "GeForce FX Go5600" }, + { 0x031b, "GeForce FX Go5650" }, + { 0x031c, "Quadro FX Go700" }, + { 0x0320, "GeForce FX 5200" }, + { 0x0321, "GeForce FX 5200 Ultra" }, + { 0x0322, "GeForce FX 5200", nvkm_device_pci_10de_0322 }, + { 0x0323, "GeForce FX 5200LE" }, + { 0x0324, "GeForce FX Go5200" }, + { 0x0325, "GeForce FX Go5250" }, + { 0x0326, "GeForce FX 5500" }, + { 0x0327, "GeForce FX 5100" }, + { 0x0328, "GeForce FX Go5200 32M/64M" }, + { 0x032a, "Quadro NVS 55/280 PCI" }, + { 0x032b, "Quadro FX 500/FX 600" }, + { 0x032c, "GeForce FX Go53xx" }, + { 0x032d, "GeForce FX Go5100" }, + { 0x0330, "GeForce FX 5900 Ultra" }, + { 0x0331, "GeForce FX 5900" }, + { 0x0332, "GeForce FX 5900XT" }, + { 0x0333, "GeForce FX 5950 Ultra" }, + { 0x0334, "GeForce FX 5900ZT" }, + { 0x0338, "Quadro FX 3000" }, + { 0x033f, "Quadro FX 700" }, + { 0x0341, "GeForce FX 5700 Ultra" }, + { 0x0342, "GeForce FX 5700" }, + { 0x0343, "GeForce FX 5700LE" }, + { 0x0344, "GeForce FX 5700VE" }, + { 0x0347, "GeForce FX Go5700" }, + { 0x0348, "GeForce FX Go5700" }, + { 0x034c, "Quadro FX Go1000" }, + { 0x034e, "Quadro FX 1100" }, + { 0x038b, "GeForce 7650 GS" }, + { 0x0390, "GeForce 7650 GS" }, + { 0x0391, "GeForce 7600 GT" }, + { 0x0392, "GeForce 7600 GS" }, + { 0x0393, "GeForce 7300 GT" }, + { 0x0394, "GeForce 7600 LE" }, + { 0x0395, "GeForce 7300 GT" }, + { 0x0397, "GeForce Go 7700" }, + { 0x0398, "GeForce Go 7600" }, + { 0x0399, "GeForce Go 7600 GT" }, + { 0x039c, "Quadro FX 560M" }, + { 0x039e, "Quadro FX 560" }, + { 0x03d0, "GeForce 6150SE nForce 430" }, + { 0x03d1, "GeForce 6100 nForce 405" }, + { 0x03d2, "GeForce 6100 nForce 400" }, + { 0x03d5, "GeForce 6100 nForce 420" }, + { 0x03d6, "GeForce 7025 / nForce 630a" }, + { 0x0400, "GeForce 8600 GTS" }, + { 0x0401, "GeForce 8600 GT" }, + { 0x0402, "GeForce 8600 GT" }, + { 0x0403, "GeForce 8600 GS" }, + { 0x0404, "GeForce 8400 GS" }, + { 0x0405, "GeForce 9500M GS" }, + { 0x0406, "GeForce 8300 GS" }, + { 0x0407, "GeForce 8600M GT" }, + { 0x0408, "GeForce 9650M GS" }, + { 0x0409, "GeForce 8700M GT" }, + { 0x040a, "Quadro FX 370" }, + { 0x040b, "Quadro NVS 320M" }, + { 0x040c, "Quadro FX 570M" }, + { 0x040d, "Quadro FX 1600M" }, + { 0x040e, "Quadro FX 570" }, + { 0x040f, "Quadro FX 1700" }, + { 0x0410, "GeForce GT 330" }, + { 0x0420, "GeForce 8400 SE" }, + { 0x0421, "GeForce 8500 GT" }, + { 0x0422, "GeForce 8400 GS" }, + { 0x0423, "GeForce 8300 GS" }, + { 0x0424, "GeForce 8400 GS" }, + { 0x0425, "GeForce 8600M GS" }, + { 0x0426, "GeForce 8400M GT" }, + { 0x0427, "GeForce 8400M GS" }, + { 0x0428, "GeForce 8400M G" }, + { 0x0429, "Quadro NVS 140M" }, + { 0x042a, "Quadro NVS 130M" }, + { 0x042b, "Quadro NVS 135M" }, + { 0x042c, "GeForce 9400 GT" }, + { 0x042d, "Quadro FX 360M" }, + { 0x042e, "GeForce 9300M G" }, + { 0x042f, "Quadro NVS 290" }, + { 0x0531, "GeForce 7150M / nForce 630M" }, + { 0x0533, "GeForce 7000M / nForce 610M" }, + { 0x053a, "GeForce 7050 PV / nForce 630a" }, + { 0x053b, "GeForce 7050 PV / nForce 630a" }, + { 0x053e, "GeForce 7025 / nForce 630a" }, + { 0x05e0, "GeForce GTX 295" }, + { 0x05e1, "GeForce GTX 280" }, + { 0x05e2, "GeForce GTX 260" }, + { 0x05e3, "GeForce GTX 285" }, + { 0x05e6, "GeForce GTX 275" }, + { 0x05e7, "Tesla C1060", nvkm_device_pci_10de_05e7 }, + { 0x05ea, "GeForce GTX 260" }, + { 0x05eb, "GeForce GTX 295" }, + { 0x05ed, "Quadroplex 2200 D2" }, + { 0x05f8, "Quadroplex 2200 S4" }, + { 0x05f9, "Quadro CX" }, + { 0x05fd, "Quadro FX 5800" }, + { 0x05fe, "Quadro FX 4800" }, + { 0x05ff, "Quadro FX 3800" }, + { 0x0600, "GeForce 8800 GTS 512" }, + { 0x0601, "GeForce 9800 GT" }, + { 0x0602, "GeForce 8800 GT" }, + { 0x0603, "GeForce GT 230" }, + { 0x0604, "GeForce 9800 GX2" }, + { 0x0605, "GeForce 9800 GT" }, + { 0x0606, "GeForce 8800 GS" }, + { 0x0607, "GeForce GTS 240" }, + { 0x0608, "GeForce 9800M GTX" }, + { 0x0609, "GeForce 8800M GTS", nvkm_device_pci_10de_0609 }, + { 0x060a, "GeForce GTX 280M" }, + { 0x060b, "GeForce 9800M GT" }, + { 0x060c, "GeForce 8800M GTX" }, + { 0x060d, "GeForce 8800 GS" }, + { 0x060f, "GeForce GTX 285M" }, + { 0x0610, "GeForce 9600 GSO" }, + { 0x0611, "GeForce 8800 GT" }, + { 0x0612, "GeForce 9800 GTX/9800 GTX+" }, + { 0x0613, "GeForce 9800 GTX+" }, + { 0x0614, "GeForce 9800 GT" }, + { 0x0615, "GeForce GTS 250" }, + { 0x0617, "GeForce 9800M GTX" }, + { 0x0618, "GeForce GTX 260M" }, + { 0x0619, "Quadro FX 4700 X2" }, + { 0x061a, "Quadro FX 3700" }, + { 0x061b, "Quadro VX 200" }, + { 0x061c, "Quadro FX 3600M" }, + { 0x061d, "Quadro FX 2800M" }, + { 0x061e, "Quadro FX 3700M" }, + { 0x061f, "Quadro FX 3800M" }, + { 0x0621, "GeForce GT 230" }, + { 0x0622, "GeForce 9600 GT" }, + { 0x0623, "GeForce 9600 GS" }, + { 0x0625, "GeForce 9600 GSO 512" }, + { 0x0626, "GeForce GT 130" }, + { 0x0627, "GeForce GT 140" }, + { 0x0628, "GeForce 9800M GTS" }, + { 0x062a, "GeForce 9700M GTS" }, + { 0x062b, "GeForce 9800M GS" }, + { 0x062c, "GeForce 9800M GTS" }, + { 0x062d, "GeForce 9600 GT" }, + { 0x062e, "GeForce 9600 GT", nvkm_device_pci_10de_062e }, + { 0x0630, "GeForce 9700 S" }, + { 0x0631, "GeForce GTS 160M" }, + { 0x0632, "GeForce GTS 150M" }, + { 0x0635, "GeForce 9600 GSO" }, + { 0x0637, "GeForce 9600 GT" }, + { 0x0638, "Quadro FX 1800" }, + { 0x063a, "Quadro FX 2700M" }, + { 0x0640, "GeForce 9500 GT" }, + { 0x0641, "GeForce 9400 GT" }, + { 0x0643, "GeForce 9500 GT" }, + { 0x0644, "GeForce 9500 GS" }, + { 0x0645, "GeForce 9500 GS" }, + { 0x0646, "GeForce GT 120" }, + { 0x0647, "GeForce 9600M GT" }, + { 0x0648, "GeForce 9600M GS" }, + { 0x0649, "GeForce 9600M GT", nvkm_device_pci_10de_0649 }, + { 0x064a, "GeForce 9700M GT" }, + { 0x064b, "GeForce 9500M G" }, + { 0x064c, "GeForce 9650M GT" }, + { 0x0651, "GeForce G 110M" }, + { 0x0652, "GeForce GT 130M", nvkm_device_pci_10de_0652 }, + { 0x0653, "GeForce GT 120M" }, + { 0x0654, "GeForce GT 220M", nvkm_device_pci_10de_0654 }, + { 0x0655, NULL, nvkm_device_pci_10de_0655 }, + { 0x0656, NULL, nvkm_device_pci_10de_0656 }, + { 0x0658, "Quadro FX 380" }, + { 0x0659, "Quadro FX 580" }, + { 0x065a, "Quadro FX 1700M" }, + { 0x065b, "GeForce 9400 GT" }, + { 0x065c, "Quadro FX 770M" }, + { 0x06c0, "GeForce GTX 480" }, + { 0x06c4, "GeForce GTX 465" }, + { 0x06ca, "GeForce GTX 480M" }, + { 0x06cd, "GeForce GTX 470" }, + { 0x06d1, "Tesla C2050 / C2070", nvkm_device_pci_10de_06d1 }, + { 0x06d2, "Tesla M2070", nvkm_device_pci_10de_06d2 }, + { 0x06d8, "Quadro 6000" }, + { 0x06d9, "Quadro 5000" }, + { 0x06da, "Quadro 5000M" }, + { 0x06dc, "Quadro 6000" }, + { 0x06dd, "Quadro 4000" }, + { 0x06de, "Tesla T20 Processor", nvkm_device_pci_10de_06de }, + { 0x06df, "Tesla M2070-Q" }, + { 0x06e0, "GeForce 9300 GE" }, + { 0x06e1, "GeForce 9300 GS" }, + { 0x06e2, "GeForce 8400" }, + { 0x06e3, "GeForce 8400 SE" }, + { 0x06e4, "GeForce 8400 GS" }, + { 0x06e5, "GeForce 9300M GS" }, + { 0x06e6, "GeForce G100" }, + { 0x06e7, "GeForce 9300 SE" }, + { 0x06e8, "GeForce 9200M GS", nvkm_device_pci_10de_06e8 }, + { 0x06e9, "GeForce 9300M GS" }, + { 0x06ea, "Quadro NVS 150M" }, + { 0x06eb, "Quadro NVS 160M" }, + { 0x06ec, "GeForce G 105M" }, + { 0x06ef, "GeForce G 103M" }, + { 0x06f1, "GeForce G105M" }, + { 0x06f8, "Quadro NVS 420" }, + { 0x06f9, "Quadro FX 370 LP", nvkm_device_pci_10de_06f9 }, + { 0x06fa, "Quadro NVS 450" }, + { 0x06fb, "Quadro FX 370M" }, + { 0x06fd, "Quadro NVS 295" }, + { 0x06ff, "HICx16 + Graphics", nvkm_device_pci_10de_06ff }, + { 0x07e0, "GeForce 7150 / nForce 630i" }, + { 0x07e1, "GeForce 7100 / nForce 630i" }, + { 0x07e2, "GeForce 7050 / nForce 630i" }, + { 0x07e3, "GeForce 7050 / nForce 610i" }, + { 0x07e5, "GeForce 7050 / nForce 620i" }, + { 0x0840, "GeForce 8200M" }, + { 0x0844, "GeForce 9100M G" }, + { 0x0845, "GeForce 8200M G" }, + { 0x0846, "GeForce 9200" }, + { 0x0847, "GeForce 9100" }, + { 0x0848, "GeForce 8300" }, + { 0x0849, "GeForce 8200" }, + { 0x084a, "nForce 730a" }, + { 0x084b, "GeForce 9200" }, + { 0x084c, "nForce 980a/780a SLI" }, + { 0x084d, "nForce 750a SLI" }, + { 0x084f, "GeForce 8100 / nForce 720a" }, + { 0x0860, "GeForce 9400" }, + { 0x0861, "GeForce 9400" }, + { 0x0862, "GeForce 9400M G" }, + { 0x0863, "GeForce 9400M" }, + { 0x0864, "GeForce 9300" }, + { 0x0865, "ION" }, + { 0x0866, "GeForce 9400M G", nvkm_device_pci_10de_0866 }, + { 0x0867, "GeForce 9400" }, + { 0x0868, "nForce 760i SLI" }, + { 0x0869, "GeForce 9400" }, + { 0x086a, "GeForce 9400" }, + { 0x086c, "GeForce 9300 / nForce 730i" }, + { 0x086d, "GeForce 9200" }, + { 0x086e, "GeForce 9100M G" }, + { 0x086f, "GeForce 8200M G" }, + { 0x0870, "GeForce 9400M" }, + { 0x0871, "GeForce 9200" }, + { 0x0872, "GeForce G102M", nvkm_device_pci_10de_0872 }, + { 0x0873, "GeForce G102M", nvkm_device_pci_10de_0873 }, + { 0x0874, "ION" }, + { 0x0876, "ION" }, + { 0x087a, "GeForce 9400" }, + { 0x087d, "ION" }, + { 0x087e, "ION LE" }, + { 0x087f, "ION LE" }, + { 0x08a0, "GeForce 320M" }, + { 0x08a2, "GeForce 320M" }, + { 0x08a3, "GeForce 320M" }, + { 0x08a4, "GeForce 320M" }, + { 0x08a5, "GeForce 320M" }, + { 0x0a20, "GeForce GT 220" }, + { 0x0a22, "GeForce 315" }, + { 0x0a23, "GeForce 210" }, + { 0x0a26, "GeForce 405" }, + { 0x0a27, "GeForce 405" }, + { 0x0a28, "GeForce GT 230M" }, + { 0x0a29, "GeForce GT 330M" }, + { 0x0a2a, "GeForce GT 230M" }, + { 0x0a2b, "GeForce GT 330M" }, + { 0x0a2c, "NVS 5100M" }, + { 0x0a2d, "GeForce GT 320M" }, + { 0x0a32, "GeForce GT 415" }, + { 0x0a34, "GeForce GT 240M" }, + { 0x0a35, "GeForce GT 325M" }, + { 0x0a38, "Quadro 400" }, + { 0x0a3c, "Quadro FX 880M" }, + { 0x0a60, "GeForce G210" }, + { 0x0a62, "GeForce 205" }, + { 0x0a63, "GeForce 310" }, + { 0x0a64, "Second Generation ION" }, + { 0x0a65, "GeForce 210" }, + { 0x0a66, "GeForce 310" }, + { 0x0a67, "GeForce 315" }, + { 0x0a68, "GeForce G105M" }, + { 0x0a69, "GeForce G105M" }, + { 0x0a6a, "NVS 2100M" }, + { 0x0a6c, "NVS 3100M" }, + { 0x0a6e, "GeForce 305M", nvkm_device_pci_10de_0a6e }, + { 0x0a6f, "Second Generation ION" }, + { 0x0a70, "GeForce 310M", nvkm_device_pci_10de_0a70 }, + { 0x0a71, "GeForce 305M" }, + { 0x0a72, "GeForce 310M" }, + { 0x0a73, "GeForce 305M", nvkm_device_pci_10de_0a73 }, + { 0x0a74, "GeForce G210M", nvkm_device_pci_10de_0a74 }, + { 0x0a75, "GeForce 310M", nvkm_device_pci_10de_0a75 }, + { 0x0a76, "Second Generation ION" }, + { 0x0a78, "Quadro FX 380 LP" }, + { 0x0a7a, "GeForce 315M", nvkm_device_pci_10de_0a7a }, + { 0x0a7c, "Quadro FX 380M" }, + { 0x0ca0, "GeForce GT 330" }, + { 0x0ca2, "GeForce GT 320" }, + { 0x0ca3, "GeForce GT 240" }, + { 0x0ca4, "GeForce GT 340" }, + { 0x0ca5, "GeForce GT 220" }, + { 0x0ca7, "GeForce GT 330" }, + { 0x0ca8, "GeForce GTS 260M" }, + { 0x0ca9, "GeForce GTS 250M" }, + { 0x0cac, "GeForce GT 220" }, + { 0x0caf, "GeForce GT 335M" }, + { 0x0cb0, "GeForce GTS 350M" }, + { 0x0cb1, "GeForce GTS 360M" }, + { 0x0cbc, "Quadro FX 1800M" }, + { 0x0dc0, "GeForce GT 440" }, + { 0x0dc4, "GeForce GTS 450" }, + { 0x0dc5, "GeForce GTS 450" }, + { 0x0dc6, "GeForce GTS 450" }, + { 0x0dcd, "GeForce GT 555M" }, + { 0x0dce, "GeForce GT 555M" }, + { 0x0dd1, "GeForce GTX 460M" }, + { 0x0dd2, "GeForce GT 445M" }, + { 0x0dd3, "GeForce GT 435M" }, + { 0x0dd6, "GeForce GT 550M" }, + { 0x0dd8, "Quadro 2000", nvkm_device_pci_10de_0dd8 }, + { 0x0dda, "Quadro 2000M" }, + { 0x0de0, "GeForce GT 440" }, + { 0x0de1, "GeForce GT 430" }, + { 0x0de2, "GeForce GT 420" }, + { 0x0de3, "GeForce GT 635M" }, + { 0x0de4, "GeForce GT 520" }, + { 0x0de5, "GeForce GT 530" }, + { 0x0de7, "GeForce GT 610" }, + { 0x0de8, "GeForce GT 620M" }, + { 0x0de9, "GeForce GT 630M", nvkm_device_pci_10de_0de9 }, + { 0x0dea, "GeForce 610M", nvkm_device_pci_10de_0dea }, + { 0x0deb, "GeForce GT 555M" }, + { 0x0dec, "GeForce GT 525M" }, + { 0x0ded, "GeForce GT 520M" }, + { 0x0dee, "GeForce GT 415M" }, + { 0x0def, "NVS 5400M" }, + { 0x0df0, "GeForce GT 425M" }, + { 0x0df1, "GeForce GT 420M" }, + { 0x0df2, "GeForce GT 435M" }, + { 0x0df3, "GeForce GT 420M" }, + { 0x0df4, "GeForce GT 540M", nvkm_device_pci_10de_0df4 }, + { 0x0df5, "GeForce GT 525M" }, + { 0x0df6, "GeForce GT 550M" }, + { 0x0df7, "GeForce GT 520M" }, + { 0x0df8, "Quadro 600" }, + { 0x0df9, "Quadro 500M" }, + { 0x0dfa, "Quadro 1000M" }, + { 0x0dfc, "NVS 5200M" }, + { 0x0e22, "GeForce GTX 460" }, + { 0x0e23, "GeForce GTX 460 SE" }, + { 0x0e24, "GeForce GTX 460" }, + { 0x0e30, "GeForce GTX 470M" }, + { 0x0e31, "GeForce GTX 485M" }, + { 0x0e3a, "Quadro 3000M" }, + { 0x0e3b, "Quadro 4000M" }, + { 0x0f00, "GeForce GT 630" }, + { 0x0f01, "GeForce GT 620" }, + { 0x0f02, "GeForce GT 730" }, + { 0x0fc0, "GeForce GT 640" }, + { 0x0fc1, "GeForce GT 640" }, + { 0x0fc2, "GeForce GT 630" }, + { 0x0fc6, "GeForce GTX 650" }, + { 0x0fc8, "GeForce GT 740" }, + { 0x0fc9, "GeForce GT 730" }, + { 0x0fcd, "GeForce GT 755M" }, + { 0x0fce, "GeForce GT 640M LE" }, + { 0x0fd1, "GeForce GT 650M" }, + { 0x0fd2, "GeForce GT 640M", nvkm_device_pci_10de_0fd2 }, + { 0x0fd3, "GeForce GT 640M LE" }, + { 0x0fd4, "GeForce GTX 660M" }, + { 0x0fd5, "GeForce GT 650M" }, + { 0x0fd8, "GeForce GT 640M" }, + { 0x0fd9, "GeForce GT 645M" }, + { 0x0fdf, "GeForce GT 740M" }, + { 0x0fe0, "GeForce GTX 660M" }, + { 0x0fe1, "GeForce GT 730M" }, + { 0x0fe2, "GeForce GT 745M" }, + { 0x0fe3, "GeForce GT 745M", nvkm_device_pci_10de_0fe3 }, + { 0x0fe4, "GeForce GT 750M" }, + { 0x0fe9, "GeForce GT 750M" }, + { 0x0fea, "GeForce GT 755M" }, + { 0x0fec, "GeForce 710A" }, + { 0x0fef, "GRID K340" }, + { 0x0ff2, "GRID K1" }, + { 0x0ff3, "Quadro K420" }, + { 0x0ff6, "Quadro K1100M" }, + { 0x0ff8, "Quadro K500M" }, + { 0x0ff9, "Quadro K2000D" }, + { 0x0ffa, "Quadro K600" }, + { 0x0ffb, "Quadro K2000M" }, + { 0x0ffc, "Quadro K1000M" }, + { 0x0ffd, "NVS 510" }, + { 0x0ffe, "Quadro K2000" }, + { 0x0fff, "Quadro 410" }, + { 0x1001, "GeForce GTX TITAN Z" }, + { 0x1004, "GeForce GTX 780" }, + { 0x1005, "GeForce GTX TITAN" }, + { 0x1007, "GeForce GTX 780" }, + { 0x1008, "GeForce GTX 780 Ti" }, + { 0x100a, "GeForce GTX 780 Ti" }, + { 0x100c, "GeForce GTX TITAN Black" }, + { 0x1021, "Tesla K20Xm" }, + { 0x1022, "Tesla K20c" }, + { 0x1023, "Tesla K40m" }, + { 0x1024, "Tesla K40c" }, + { 0x1026, "Tesla K20s" }, + { 0x1027, "Tesla K40st" }, + { 0x1028, "Tesla K20m" }, + { 0x1029, "Tesla K40s" }, + { 0x102a, "Tesla K40t" }, + { 0x102d, "Tesla K80" }, + { 0x103a, "Quadro K6000" }, + { 0x103c, "Quadro K5200" }, + { 0x1040, "GeForce GT 520" }, + { 0x1042, "GeForce 510" }, + { 0x1048, "GeForce 605" }, + { 0x1049, "GeForce GT 620" }, + { 0x104a, "GeForce GT 610" }, + { 0x104b, "GeForce GT 625 (OEM)", nvkm_device_pci_10de_104b }, + { 0x104c, "GeForce GT 705" }, + { 0x1050, "GeForce GT 520M" }, + { 0x1051, "GeForce GT 520MX" }, + { 0x1052, "GeForce GT 520M" }, + { 0x1054, "GeForce 410M" }, + { 0x1055, "GeForce 410M" }, + { 0x1056, "NVS 4200M" }, + { 0x1057, "NVS 4200M" }, + { 0x1058, "GeForce 610M", nvkm_device_pci_10de_1058 }, + { 0x1059, "GeForce 610M" }, + { 0x105a, "GeForce 610M" }, + { 0x105b, "GeForce 705M", nvkm_device_pci_10de_105b }, + { 0x107c, "NVS 315" }, + { 0x107d, "NVS 310" }, + { 0x1080, "GeForce GTX 580" }, + { 0x1081, "GeForce GTX 570" }, + { 0x1082, "GeForce GTX 560 Ti" }, + { 0x1084, "GeForce GTX 560" }, + { 0x1086, "GeForce GTX 570" }, + { 0x1087, "GeForce GTX 560 Ti" }, + { 0x1088, "GeForce GTX 590" }, + { 0x1089, "GeForce GTX 580" }, + { 0x108b, "GeForce GTX 580" }, + { 0x1091, "Tesla M2090", nvkm_device_pci_10de_1091 }, + { 0x1094, "Tesla M2075" }, + { 0x1096, "Tesla C2075", nvkm_device_pci_10de_1096 }, + { 0x109a, "Quadro 5010M" }, + { 0x109b, "Quadro 7000" }, + { 0x10c0, "GeForce 9300 GS" }, + { 0x10c3, "GeForce 8400GS" }, + { 0x10c5, "GeForce 405" }, + { 0x10d8, "NVS 300" }, + { 0x1140, NULL, nvkm_device_pci_10de_1140 }, + { 0x1180, "GeForce GTX 680" }, + { 0x1183, "GeForce GTX 660 Ti" }, + { 0x1184, "GeForce GTX 770" }, + { 0x1185, "GeForce GTX 660", nvkm_device_pci_10de_1185 }, + { 0x1187, "GeForce GTX 760" }, + { 0x1188, "GeForce GTX 690" }, + { 0x1189, "GeForce GTX 670", nvkm_device_pci_10de_1189 }, + { 0x118a, "GRID K520" }, + { 0x118e, "GeForce GTX 760 (192-bit)" }, + { 0x118f, "Tesla K10" }, + { 0x1193, "GeForce GTX 760 Ti OEM" }, + { 0x1194, "Tesla K8" }, + { 0x1195, "GeForce GTX 660" }, + { 0x1198, "GeForce GTX 880M" }, + { 0x1199, "GeForce GTX 870M", nvkm_device_pci_10de_1199 }, + { 0x119a, "GeForce GTX 860M" }, + { 0x119d, "GeForce GTX 775M" }, + { 0x119e, "GeForce GTX 780M" }, + { 0x119f, "GeForce GTX 780M" }, + { 0x11a0, "GeForce GTX 680M" }, + { 0x11a1, "GeForce GTX 670MX" }, + { 0x11a2, "GeForce GTX 675MX" }, + { 0x11a3, "GeForce GTX 680MX" }, + { 0x11a7, "GeForce GTX 675MX" }, + { 0x11b4, "Quadro K4200" }, + { 0x11b6, "Quadro K3100M" }, + { 0x11b7, "Quadro K4100M" }, + { 0x11b8, "Quadro K5100M" }, + { 0x11ba, "Quadro K5000" }, + { 0x11bc, "Quadro K5000M" }, + { 0x11bd, "Quadro K4000M" }, + { 0x11be, "Quadro K3000M" }, + { 0x11bf, "GRID K2" }, + { 0x11c0, "GeForce GTX 660" }, + { 0x11c2, "GeForce GTX 650 Ti BOOST" }, + { 0x11c3, "GeForce GTX 650 Ti" }, + { 0x11c4, "GeForce GTX 645" }, + { 0x11c5, "GeForce GT 740" }, + { 0x11c6, "GeForce GTX 650 Ti" }, + { 0x11c8, "GeForce GTX 650" }, + { 0x11cb, "GeForce GT 740" }, + { 0x11e0, "GeForce GTX 770M" }, + { 0x11e1, "GeForce GTX 765M" }, + { 0x11e2, "GeForce GTX 765M" }, + { 0x11e3, "GeForce GTX 760M", nvkm_device_pci_10de_11e3 }, + { 0x11fa, "Quadro K4000" }, + { 0x11fc, "Quadro K2100M" }, + { 0x1200, "GeForce GTX 560 Ti" }, + { 0x1201, "GeForce GTX 560" }, + { 0x1203, "GeForce GTX 460 SE v2" }, + { 0x1205, "GeForce GTX 460 v2" }, + { 0x1206, "GeForce GTX 555" }, + { 0x1207, "GeForce GT 645" }, + { 0x1208, "GeForce GTX 560 SE" }, + { 0x1210, "GeForce GTX 570M" }, + { 0x1211, "GeForce GTX 580M" }, + { 0x1212, "GeForce GTX 675M" }, + { 0x1213, "GeForce GTX 670M" }, + { 0x1241, "GeForce GT 545" }, + { 0x1243, "GeForce GT 545" }, + { 0x1244, "GeForce GTX 550 Ti" }, + { 0x1245, "GeForce GTS 450" }, + { 0x1246, "GeForce GT 550M" }, + { 0x1247, "GeForce GT 555M", nvkm_device_pci_10de_1247 }, + { 0x1248, "GeForce GT 555M" }, + { 0x1249, "GeForce GTS 450" }, + { 0x124b, "GeForce GT 640" }, + { 0x124d, "GeForce GT 555M", nvkm_device_pci_10de_124d }, + { 0x1251, "GeForce GTX 560M" }, + { 0x1280, "GeForce GT 635" }, + { 0x1281, "GeForce GT 710" }, + { 0x1282, "GeForce GT 640" }, + { 0x1284, "GeForce GT 630" }, + { 0x1286, "GeForce GT 720" }, + { 0x1287, "GeForce GT 730" }, + { 0x1288, "GeForce GT 720" }, + { 0x1289, "GeForce GT 710" }, + { 0x1290, "GeForce GT 730M", nvkm_device_pci_10de_1290 }, + { 0x1291, "GeForce GT 735M" }, + { 0x1292, "GeForce GT 740M", nvkm_device_pci_10de_1292 }, + { 0x1293, "GeForce GT 730M" }, + { 0x1295, "GeForce 710M", nvkm_device_pci_10de_1295 }, + { 0x1296, "GeForce 825M" }, + { 0x1298, "GeForce GT 720M" }, + { 0x1299, "GeForce 920M", nvkm_device_pci_10de_1299 }, + { 0x129a, "GeForce 910M" }, + { 0x12b9, "Quadro K610M" }, + { 0x12ba, "Quadro K510M" }, + { 0x1340, "GeForce 830M", nvkm_device_pci_10de_1340 }, + { 0x1341, "GeForce 840M", nvkm_device_pci_10de_1341 }, + { 0x1344, "GeForce 845M" }, + { 0x1346, "GeForce 930M", nvkm_device_pci_10de_1346 }, + { 0x1347, "GeForce 940M", nvkm_device_pci_10de_1347 }, + { 0x137a, NULL, nvkm_device_pci_10de_137a }, + { 0x137d, NULL, nvkm_device_pci_10de_137d }, + { 0x1380, "GeForce GTX 750 Ti" }, + { 0x1381, "GeForce GTX 750" }, + { 0x1382, "GeForce GTX 745" }, + { 0x1390, "GeForce 845M" }, + { 0x1391, "GeForce GTX 850M", nvkm_device_pci_10de_1391 }, + { 0x1392, "GeForce GTX 860M", nvkm_device_pci_10de_1392 }, + { 0x1393, "GeForce 840M" }, + { 0x1398, "GeForce 845M" }, + { 0x139a, "GeForce GTX 950M", nvkm_device_pci_10de_139a }, + { 0x139b, "GeForce GTX 960M", nvkm_device_pci_10de_139b }, + { 0x139c, "GeForce 940M" }, + { 0x13b3, "Quadro K2200M" }, + { 0x13ba, "Quadro K2200" }, + { 0x13bb, "Quadro K620" }, + { 0x13bc, "Quadro K1200" }, + { 0x13c0, "GeForce GTX 980" }, + { 0x13c2, "GeForce GTX 970" }, + { 0x13d7, "GeForce GTX 980M" }, + { 0x13d8, "GeForce GTX 970M" }, + { 0x13d9, "GeForce GTX 965M" }, + { 0x1401, "GeForce GTX 960" }, + { 0x1617, "GeForce GTX 980M" }, + { 0x1618, "GeForce GTX 970M" }, + { 0x1619, "GeForce GTX 965M" }, + { 0x17c2, "GeForce GTX TITAN X" }, + { 0x17c8, "GeForce GTX 980 Ti" }, + { 0x17f0, "Quadro M6000" }, + {} +}; + +static struct nvkm_device_pci * +nvkm_device_pci(struct nvkm_device *device) +{ + return container_of(device, struct nvkm_device_pci, device); +} + +static resource_size_t +nvkm_device_pci_resource_addr(struct nvkm_device *device, unsigned bar) +{ + struct nvkm_device_pci *pdev = nvkm_device_pci(device); + return pci_resource_start(pdev->pdev, bar); +} + +static resource_size_t +nvkm_device_pci_resource_size(struct nvkm_device *device, unsigned bar) +{ + struct nvkm_device_pci *pdev = nvkm_device_pci(device); + return pci_resource_len(pdev->pdev, bar); +} + +static void +nvkm_device_pci_fini(struct nvkm_device *device, bool suspend) +{ + struct nvkm_device_pci *pdev = nvkm_device_pci(device); + if (suspend) { + pci_disable_device(pdev->pdev); + pdev->suspend = true; + } +} + +static int +nvkm_device_pci_preinit(struct nvkm_device *device) +{ + struct nvkm_device_pci *pdev = nvkm_device_pci(device); + if (pdev->suspend) { + int ret = pci_enable_device(pdev->pdev); + if (ret) + return ret; + pci_set_master(pdev->pdev); + pdev->suspend = false; + } + return 0; +} + +static void * +nvkm_device_pci_dtor(struct nvkm_device *device) +{ + struct nvkm_device_pci *pdev = nvkm_device_pci(device); + pci_disable_device(pdev->pdev); + return pdev; +} + +static const struct nvkm_device_func +nvkm_device_pci_func = { + .pci = nvkm_device_pci, + .dtor = nvkm_device_pci_dtor, + .preinit = nvkm_device_pci_preinit, + .fini = nvkm_device_pci_fini, + .resource_addr = nvkm_device_pci_resource_addr, + .resource_size = nvkm_device_pci_resource_size, + .cpu_coherent = !IS_ENABLED(CONFIG_ARM), +}; + +int +nvkm_device_pci_new(struct pci_dev *pci_dev, const char *cfg, const char *dbg, + bool detect, bool mmio, u64 subdev_mask, + struct nvkm_device **pdevice) +{ + const struct nvkm_device_quirk *quirk = NULL; + const struct nvkm_device_pci_device *pcid; + const struct nvkm_device_pci_vendor *pciv; + const char *name = NULL; + struct nvkm_device_pci *pdev; + int ret, bits; + + ret = pci_enable_device(pci_dev); + if (ret) + return ret; + + switch (pci_dev->vendor) { + case 0x10de: pcid = nvkm_device_pci_10de; break; + default: + pcid = NULL; + break; + } + + while (pcid && pcid->device) { + if (pciv = pcid->vendor, pcid->device == pci_dev->device) { + while (pciv && pciv->vendor) { + if (pciv->vendor == pci_dev->subsystem_vendor && + pciv->device == pci_dev->subsystem_device) { + quirk = &pciv->quirk; + name = pciv->name; + break; + } + pciv++; + } + if (!name) + name = pcid->name; + break; + } + pcid++; + } + + if (!(pdev = kzalloc(sizeof(*pdev), GFP_KERNEL))) { + pci_disable_device(pci_dev); + return -ENOMEM; + } + *pdevice = &pdev->device; + pdev->pdev = pci_dev; + + ret = nvkm_device_ctor(&nvkm_device_pci_func, quirk, &pci_dev->dev, + pci_is_pcie(pci_dev) ? NVKM_DEVICE_PCIE : + pci_find_capability(pci_dev, PCI_CAP_ID_AGP) ? + NVKM_DEVICE_AGP : NVKM_DEVICE_PCI, + (u64)pci_domain_nr(pci_dev->bus) << 32 | + pci_dev->bus->number << 16 | + PCI_SLOT(pci_dev->devfn) << 8 | + PCI_FUNC(pci_dev->devfn), name, + cfg, dbg, detect, mmio, subdev_mask, + &pdev->device); + + if (ret) + return ret; + + /* Set DMA mask based on capabilities reported by the MMU subdev. */ + if (pdev->device.mmu && !pdev->device.pci->agp.bridge) + bits = pdev->device.mmu->dma_bits; + else + bits = 32; + + ret = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(bits)); + if (ret && bits != 32) { + dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); + pdev->device.mmu->dma_bits = 32; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h new file mode 100644 index 000000000..93949b3c7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DEVICE_PRIV_H__ +#define __NVKM_DEVICE_PRIV_H__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int nvkm_device_ctor(const struct nvkm_device_func *, + const struct nvkm_device_quirk *, + struct device *, enum nvkm_device_type, u64 handle, + const char *name, const char *cfg, const char *dbg, + bool detect, bool mmio, u64 subdev_mask, + struct nvkm_device *); +int nvkm_device_init(struct nvkm_device *); +int nvkm_device_fini(struct nvkm_device *, bool suspend); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c new file mode 100644 index 000000000..ac9e12258 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#ifdef CONFIG_NOUVEAU_PLATFORM_DRIVER +#include "priv.h" + +#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) +#include +#endif + +static int +nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev) +{ + int ret; + + if (tdev->vdd) { + ret = regulator_enable(tdev->vdd); + if (ret) + goto err_power; + } + + ret = clk_prepare_enable(tdev->clk); + if (ret) + goto err_clk; + ret = clk_prepare_enable(tdev->clk_ref); + if (ret) + goto err_clk_ref; + ret = clk_prepare_enable(tdev->clk_pwr); + if (ret) + goto err_clk_pwr; + clk_set_rate(tdev->clk_pwr, 204000000); + udelay(10); + + if (!tdev->pdev->dev.pm_domain) { + reset_control_assert(tdev->rst); + udelay(10); + + ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D); + if (ret) + goto err_clamp; + udelay(10); + + reset_control_deassert(tdev->rst); + udelay(10); + } + + return 0; + +err_clamp: + clk_disable_unprepare(tdev->clk_pwr); +err_clk_pwr: + clk_disable_unprepare(tdev->clk_ref); +err_clk_ref: + clk_disable_unprepare(tdev->clk); +err_clk: + if (tdev->vdd) + regulator_disable(tdev->vdd); +err_power: + return ret; +} + +static int +nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev) +{ + int ret; + + clk_disable_unprepare(tdev->clk_pwr); + clk_disable_unprepare(tdev->clk_ref); + clk_disable_unprepare(tdev->clk); + udelay(10); + + if (tdev->vdd) { + ret = regulator_disable(tdev->vdd); + if (ret) + return ret; + } + + return 0; +} + +static void +nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev) +{ +#if IS_ENABLED(CONFIG_IOMMU_API) + struct device *dev = &tdev->pdev->dev; + unsigned long pgsize_bitmap; + int ret; + +#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) + if (dev->archdata.mapping) { + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); + } +#endif + + if (!tdev->func->iommu_bit) + return; + + mutex_init(&tdev->iommu.mutex); + + if (device_iommu_mapped(dev)) { + tdev->iommu.domain = iommu_domain_alloc(&platform_bus_type); + if (!tdev->iommu.domain) + goto error; + + /* + * A IOMMU is only usable if it supports page sizes smaller + * or equal to the system's PAGE_SIZE, with a preference if + * both are equal. + */ + pgsize_bitmap = tdev->iommu.domain->pgsize_bitmap; + if (pgsize_bitmap & PAGE_SIZE) { + tdev->iommu.pgshift = PAGE_SHIFT; + } else { + tdev->iommu.pgshift = fls(pgsize_bitmap & ~PAGE_MASK); + if (tdev->iommu.pgshift == 0) { + dev_warn(dev, "unsupported IOMMU page size\n"); + goto free_domain; + } + tdev->iommu.pgshift -= 1; + } + + ret = iommu_attach_device(tdev->iommu.domain, dev); + if (ret) + goto free_domain; + + ret = nvkm_mm_init(&tdev->iommu.mm, 0, 0, + (1ULL << tdev->func->iommu_bit) >> + tdev->iommu.pgshift, 1); + if (ret) + goto detach_device; + } + + return; + +detach_device: + iommu_detach_device(tdev->iommu.domain, dev); + +free_domain: + iommu_domain_free(tdev->iommu.domain); + +error: + tdev->iommu.domain = NULL; + tdev->iommu.pgshift = 0; + dev_err(dev, "cannot initialize IOMMU MM\n"); +#endif +} + +static void +nvkm_device_tegra_remove_iommu(struct nvkm_device_tegra *tdev) +{ +#if IS_ENABLED(CONFIG_IOMMU_API) + if (tdev->iommu.domain) { + nvkm_mm_fini(&tdev->iommu.mm); + iommu_detach_device(tdev->iommu.domain, tdev->device.dev); + iommu_domain_free(tdev->iommu.domain); + } +#endif +} + +static struct nvkm_device_tegra * +nvkm_device_tegra(struct nvkm_device *device) +{ + return container_of(device, struct nvkm_device_tegra, device); +} + +static struct resource * +nvkm_device_tegra_resource(struct nvkm_device *device, unsigned bar) +{ + struct nvkm_device_tegra *tdev = nvkm_device_tegra(device); + return platform_get_resource(tdev->pdev, IORESOURCE_MEM, bar); +} + +static resource_size_t +nvkm_device_tegra_resource_addr(struct nvkm_device *device, unsigned bar) +{ + struct resource *res = nvkm_device_tegra_resource(device, bar); + return res ? res->start : 0; +} + +static resource_size_t +nvkm_device_tegra_resource_size(struct nvkm_device *device, unsigned bar) +{ + struct resource *res = nvkm_device_tegra_resource(device, bar); + return res ? resource_size(res) : 0; +} + +static irqreturn_t +nvkm_device_tegra_intr(int irq, void *arg) +{ + struct nvkm_device_tegra *tdev = arg; + struct nvkm_device *device = &tdev->device; + bool handled = false; + nvkm_mc_intr_unarm(device); + nvkm_mc_intr(device, &handled); + nvkm_mc_intr_rearm(device); + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static void +nvkm_device_tegra_fini(struct nvkm_device *device, bool suspend) +{ + struct nvkm_device_tegra *tdev = nvkm_device_tegra(device); + if (tdev->irq) { + free_irq(tdev->irq, tdev); + tdev->irq = 0; + } +} + +static int +nvkm_device_tegra_init(struct nvkm_device *device) +{ + struct nvkm_device_tegra *tdev = nvkm_device_tegra(device); + int irq, ret; + + irq = platform_get_irq_byname(tdev->pdev, "stall"); + if (irq < 0) + return irq; + + ret = request_irq(irq, nvkm_device_tegra_intr, + IRQF_SHARED, "nvkm", tdev); + if (ret) + return ret; + + tdev->irq = irq; + return 0; +} + +static void * +nvkm_device_tegra_dtor(struct nvkm_device *device) +{ + struct nvkm_device_tegra *tdev = nvkm_device_tegra(device); + nvkm_device_tegra_power_down(tdev); + nvkm_device_tegra_remove_iommu(tdev); + return tdev; +} + +static const struct nvkm_device_func +nvkm_device_tegra_func = { + .tegra = nvkm_device_tegra, + .dtor = nvkm_device_tegra_dtor, + .init = nvkm_device_tegra_init, + .fini = nvkm_device_tegra_fini, + .resource_addr = nvkm_device_tegra_resource_addr, + .resource_size = nvkm_device_tegra_resource_size, + .cpu_coherent = false, +}; + +int +nvkm_device_tegra_new(const struct nvkm_device_tegra_func *func, + struct platform_device *pdev, + const char *cfg, const char *dbg, + bool detect, bool mmio, u64 subdev_mask, + struct nvkm_device **pdevice) +{ + struct nvkm_device_tegra *tdev; + unsigned long rate; + int ret; + + if (!(tdev = kzalloc(sizeof(*tdev), GFP_KERNEL))) + return -ENOMEM; + + tdev->func = func; + tdev->pdev = pdev; + + if (func->require_vdd) { + tdev->vdd = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(tdev->vdd)) { + ret = PTR_ERR(tdev->vdd); + goto free; + } + } + + tdev->rst = devm_reset_control_get(&pdev->dev, "gpu"); + if (IS_ERR(tdev->rst)) { + ret = PTR_ERR(tdev->rst); + goto free; + } + + tdev->clk = devm_clk_get(&pdev->dev, "gpu"); + if (IS_ERR(tdev->clk)) { + ret = PTR_ERR(tdev->clk); + goto free; + } + + rate = clk_get_rate(tdev->clk); + if (rate == 0) { + ret = clk_set_rate(tdev->clk, ULONG_MAX); + if (ret < 0) + goto free; + + rate = clk_get_rate(tdev->clk); + + dev_dbg(&pdev->dev, "GPU clock set to %lu\n", rate); + } + + if (func->require_ref_clk) + tdev->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(tdev->clk_ref)) { + ret = PTR_ERR(tdev->clk_ref); + goto free; + } + + tdev->clk_pwr = devm_clk_get(&pdev->dev, "pwr"); + if (IS_ERR(tdev->clk_pwr)) { + ret = PTR_ERR(tdev->clk_pwr); + goto free; + } + + /** + * The IOMMU bit defines the upper limit of the GPU-addressable space. + */ + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(tdev->func->iommu_bit)); + if (ret) + goto free; + + nvkm_device_tegra_probe_iommu(tdev); + + ret = nvkm_device_tegra_power_up(tdev); + if (ret) + goto remove; + + tdev->gpu_speedo = tegra_sku_info.gpu_speedo_value; + tdev->gpu_speedo_id = tegra_sku_info.gpu_speedo_id; + ret = nvkm_device_ctor(&nvkm_device_tegra_func, NULL, &pdev->dev, + NVKM_DEVICE_TEGRA, pdev->id, NULL, + cfg, dbg, detect, mmio, subdev_mask, + &tdev->device); + if (ret) + goto powerdown; + + *pdevice = &tdev->device; + + return 0; + +powerdown: + nvkm_device_tegra_power_down(tdev); +remove: + nvkm_device_tegra_remove_iommu(tdev); +free: + kfree(tdev); + return ret; +} +#else +int +nvkm_device_tegra_new(const struct nvkm_device_tegra_func *func, + struct platform_device *pdev, + const char *cfg, const char *dbg, + bool detect, bool mmio, u64 subdev_mask, + struct nvkm_device **pdevice) +{ + return -ENOSYS; +} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/user.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/user.c new file mode 100644 index 000000000..45f509c11 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/user.c @@ -0,0 +1,428 @@ +/* + * 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 + */ +#define nvkm_udevice(p) container_of((p), struct nvkm_udevice, object) +#include "priv.h" +#include "ctrl.h" + +#include +#include +#include +#include + +#include +#include +#include + +struct nvkm_udevice { + struct nvkm_object object; + struct nvkm_device *device; +}; + +static int +nvkm_udevice_info_subdev(struct nvkm_device *device, u64 mthd, u64 *data) +{ + struct nvkm_subdev *subdev; + enum nvkm_subdev_type type; + + switch (mthd & NV_DEVICE_INFO_UNIT) { + case NV_DEVICE_HOST(0): type = NVKM_ENGINE_FIFO; break; + default: + return -EINVAL; + } + + subdev = nvkm_device_subdev(device, type, 0); + if (subdev) + return nvkm_subdev_info(subdev, mthd, data); + return -ENODEV; +} + +static void +nvkm_udevice_info_v1(struct nvkm_device *device, + struct nv_device_info_v1_data *args) +{ + if (args->mthd & NV_DEVICE_INFO_UNIT) { + if (nvkm_udevice_info_subdev(device, args->mthd, &args->data)) + args->mthd = NV_DEVICE_INFO_INVALID; + return; + } + args->mthd = NV_DEVICE_INFO_INVALID; +} + +static int +nvkm_udevice_info(struct nvkm_udevice *udev, void *data, u32 size) +{ + struct nvkm_object *object = &udev->object; + struct nvkm_device *device = udev->device; + struct nvkm_fb *fb = device->fb; + struct nvkm_instmem *imem = device->imem; + union { + struct nv_device_info_v0 v0; + struct nv_device_info_v1 v1; + } *args = data; + int ret = -ENOSYS, i; + + nvif_ioctl(object, "device info size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) { + nvif_ioctl(object, "device info vers %d count %d\n", + args->v1.version, args->v1.count); + if (args->v1.count * sizeof(args->v1.data[0]) == size) { + for (i = 0; i < args->v1.count; i++) + nvkm_udevice_info_v1(device, &args->v1.data[i]); + return 0; + } + return -EINVAL; + } else + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "device info vers %d\n", args->v0.version); + } else + return ret; + + switch (device->chipset) { + case 0x01a: + case 0x01f: + case 0x04c: + case 0x04e: + case 0x063: + case 0x067: + case 0x068: + case 0x0aa: + case 0x0ac: + case 0x0af: + args->v0.platform = NV_DEVICE_INFO_V0_IGP; + break; + default: + switch (device->type) { + case NVKM_DEVICE_PCI: + args->v0.platform = NV_DEVICE_INFO_V0_PCI; + break; + case NVKM_DEVICE_AGP: + args->v0.platform = NV_DEVICE_INFO_V0_AGP; + break; + case NVKM_DEVICE_PCIE: + args->v0.platform = NV_DEVICE_INFO_V0_PCIE; + break; + case NVKM_DEVICE_TEGRA: + args->v0.platform = NV_DEVICE_INFO_V0_SOC; + break; + default: + WARN_ON(1); + break; + } + break; + } + + switch (device->card_type) { + case NV_04: args->v0.family = NV_DEVICE_INFO_V0_TNT; break; + case NV_10: + case NV_11: args->v0.family = NV_DEVICE_INFO_V0_CELSIUS; break; + case NV_20: args->v0.family = NV_DEVICE_INFO_V0_KELVIN; break; + case NV_30: args->v0.family = NV_DEVICE_INFO_V0_RANKINE; break; + case NV_40: args->v0.family = NV_DEVICE_INFO_V0_CURIE; break; + case NV_50: args->v0.family = NV_DEVICE_INFO_V0_TESLA; break; + case NV_C0: args->v0.family = NV_DEVICE_INFO_V0_FERMI; break; + case NV_E0: args->v0.family = NV_DEVICE_INFO_V0_KEPLER; break; + case GM100: args->v0.family = NV_DEVICE_INFO_V0_MAXWELL; break; + case GP100: args->v0.family = NV_DEVICE_INFO_V0_PASCAL; break; + case GV100: args->v0.family = NV_DEVICE_INFO_V0_VOLTA; break; + case TU100: args->v0.family = NV_DEVICE_INFO_V0_TURING; break; + case GA100: args->v0.family = NV_DEVICE_INFO_V0_AMPERE; break; + default: + args->v0.family = 0; + break; + } + + args->v0.chipset = device->chipset; + args->v0.revision = device->chiprev; + if (fb && fb->ram) + args->v0.ram_size = args->v0.ram_user = fb->ram->size; + else + args->v0.ram_size = args->v0.ram_user = 0; + if (imem && args->v0.ram_size > 0) + args->v0.ram_user = args->v0.ram_user - imem->reserved; + + snprintf(args->v0.chip, sizeof(args->v0.chip), "%s", device->chip->name); + snprintf(args->v0.name, sizeof(args->v0.name), "%s", device->name); + return 0; +} + +static int +nvkm_udevice_time(struct nvkm_udevice *udev, void *data, u32 size) +{ + struct nvkm_object *object = &udev->object; + struct nvkm_device *device = udev->device; + union { + struct nv_device_time_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "device time size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "device time vers %d\n", args->v0.version); + args->v0.time = nvkm_timer_read(device->timer); + } + + return ret; +} + +static int +nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + nvif_ioctl(object, "device mthd %08x\n", mthd); + switch (mthd) { + case NV_DEVICE_V0_INFO: + return nvkm_udevice_info(udev, data, size); + case NV_DEVICE_V0_TIME: + return nvkm_udevice_time(udev, data, size); + default: + break; + } + return -EINVAL; +} + +static int +nvkm_udevice_rd08(struct nvkm_object *object, u64 addr, u8 *data) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + *data = nvkm_rd08(udev->device, addr); + return 0; +} + +static int +nvkm_udevice_rd16(struct nvkm_object *object, u64 addr, u16 *data) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + *data = nvkm_rd16(udev->device, addr); + return 0; +} + +static int +nvkm_udevice_rd32(struct nvkm_object *object, u64 addr, u32 *data) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + *data = nvkm_rd32(udev->device, addr); + return 0; +} + +static int +nvkm_udevice_wr08(struct nvkm_object *object, u64 addr, u8 data) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + nvkm_wr08(udev->device, addr, data); + return 0; +} + +static int +nvkm_udevice_wr16(struct nvkm_object *object, u64 addr, u16 data) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + nvkm_wr16(udev->device, addr, data); + return 0; +} + +static int +nvkm_udevice_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + nvkm_wr32(udev->device, addr, data); + return 0; +} + +static int +nvkm_udevice_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + struct nvkm_device *device = udev->device; + *type = NVKM_OBJECT_MAP_IO; + *addr = device->func->resource_addr(device, 0); + *size = device->func->resource_size(device, 0); + return 0; +} + +static int +nvkm_udevice_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + struct nvkm_device *device = udev->device; + int ret = 0; + + mutex_lock(&device->mutex); + if (!--device->refcount) { + ret = nvkm_device_fini(device, suspend); + if (ret && suspend) { + device->refcount++; + goto done; + } + } + +done: + mutex_unlock(&device->mutex); + return ret; +} + +static int +nvkm_udevice_init(struct nvkm_object *object) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + struct nvkm_device *device = udev->device; + int ret = 0; + + mutex_lock(&device->mutex); + if (!device->refcount++) { + ret = nvkm_device_init(device); + if (ret) { + device->refcount--; + goto done; + } + } + +done: + mutex_unlock(&device->mutex); + return ret; +} + +static int +nvkm_udevice_child_new(const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_udevice *udev = nvkm_udevice(oclass->parent); + const struct nvkm_device_oclass *sclass = oclass->priv; + return sclass->ctor(udev->device, oclass, data, size, pobject); +} + +static int +nvkm_udevice_child_get(struct nvkm_object *object, int index, + struct nvkm_oclass *oclass) +{ + struct nvkm_udevice *udev = nvkm_udevice(object); + struct nvkm_device *device = udev->device; + struct nvkm_engine *engine; + u64 mask = (1ULL << NVKM_ENGINE_DMAOBJ) | + (1ULL << NVKM_ENGINE_FIFO) | + (1ULL << NVKM_ENGINE_DISP) | + (1ULL << NVKM_ENGINE_PM); + const struct nvkm_device_oclass *sclass = NULL; + int i; + + for (; i = __ffs64(mask), mask && !sclass; mask &= ~(1ULL << i)) { + if (!(engine = nvkm_device_engine(device, i, 0)) || + !(engine->func->base.sclass)) + continue; + oclass->engine = engine; + + index -= engine->func->base.sclass(oclass, index, &sclass); + } + + if (!sclass) { + if (index-- == 0) + sclass = &nvkm_control_oclass; + else if (device->mmu && index-- == 0) + sclass = &device->mmu->user; + else if (device->fault && index-- == 0) + sclass = &device->fault->user; + else + return -EINVAL; + + oclass->base = sclass->base; + oclass->engine = NULL; + } + + oclass->ctor = nvkm_udevice_child_new; + oclass->priv = sclass; + return 0; +} + +static const struct nvkm_object_func +nvkm_udevice_super = { + .init = nvkm_udevice_init, + .fini = nvkm_udevice_fini, + .mthd = nvkm_udevice_mthd, + .map = nvkm_udevice_map, + .rd08 = nvkm_udevice_rd08, + .rd16 = nvkm_udevice_rd16, + .rd32 = nvkm_udevice_rd32, + .wr08 = nvkm_udevice_wr08, + .wr16 = nvkm_udevice_wr16, + .wr32 = nvkm_udevice_wr32, + .sclass = nvkm_udevice_child_get, +}; + +static const struct nvkm_object_func +nvkm_udevice = { + .init = nvkm_udevice_init, + .fini = nvkm_udevice_fini, + .mthd = nvkm_udevice_mthd, + .sclass = nvkm_udevice_child_get, +}; + +static int +nvkm_udevice_new(const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nv_device_v0 v0; + } *args = data; + struct nvkm_client *client = oclass->client; + struct nvkm_object *parent = &client->object; + const struct nvkm_object_func *func; + struct nvkm_udevice *udev; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create device size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create device v%d device %016llx\n", + args->v0.version, args->v0.device); + } else + return ret; + + /* give priviledged clients register access */ + if (args->v0.priv) + func = &nvkm_udevice_super; + else + func = &nvkm_udevice; + + if (!(udev = kzalloc(sizeof(*udev), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(func, oclass, &udev->object); + *pobject = &udev->object; + + /* find the device that matches what the client requested */ + if (args->v0.device != ~0) + udev->device = nvkm_device_find(args->v0.device); + else + udev->device = nvkm_device_find(client->device); + if (!udev->device) + return -ENODEV; + + return 0; +} + +const struct nvkm_sclass +nvkm_udevice_sclass = { + .oclass = NV_DEVICE, + .minver = 0, + .maxver = 0, + .ctor = nvkm_udevice_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild new file mode 100644 index 000000000..600072a90 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/disp/base.o +nvkm-y += nvkm/engine/disp/chan.o +nvkm-y += nvkm/engine/disp/conn.o +nvkm-y += nvkm/engine/disp/dp.o +nvkm-y += nvkm/engine/disp/hdmi.o +nvkm-y += nvkm/engine/disp/head.o +nvkm-y += nvkm/engine/disp/ior.o +nvkm-y += nvkm/engine/disp/outp.o +nvkm-y += nvkm/engine/disp/vga.o + +nvkm-y += nvkm/engine/disp/nv04.o +nvkm-y += nvkm/engine/disp/nv50.o +nvkm-y += nvkm/engine/disp/g84.o +nvkm-y += nvkm/engine/disp/g94.o +nvkm-y += nvkm/engine/disp/gt200.o +nvkm-y += nvkm/engine/disp/mcp77.o +nvkm-y += nvkm/engine/disp/gt215.o +nvkm-y += nvkm/engine/disp/mcp89.o +nvkm-y += nvkm/engine/disp/gf119.o +nvkm-y += nvkm/engine/disp/gk104.o +nvkm-y += nvkm/engine/disp/gk110.o +nvkm-y += nvkm/engine/disp/gm107.o +nvkm-y += nvkm/engine/disp/gm200.o +nvkm-y += nvkm/engine/disp/gp100.o +nvkm-y += nvkm/engine/disp/gp102.o +nvkm-y += nvkm/engine/disp/gv100.o +nvkm-y += nvkm/engine/disp/tu102.o +nvkm-y += nvkm/engine/disp/ga102.o + +nvkm-y += nvkm/engine/disp/rootnv04.o +nvkm-y += nvkm/engine/disp/rootnv50.o + +nvkm-y += nvkm/engine/disp/udisp.o +nvkm-y += nvkm/engine/disp/uconn.o +nvkm-y += nvkm/engine/disp/uoutp.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c new file mode 100644 index 000000000..65c99d948 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c @@ -0,0 +1,477 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "conn.h" +#include "dp.h" +#include "head.h" +#include "ior.h" +#include "outp.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void +nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int id) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + struct nvkm_head *head = nvkm_head_find(disp, id); + if (head) + head->func->vblank_put(head); +} + +static void +nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); + struct nvkm_head *head = nvkm_head_find(disp, id); + if (head) + head->func->vblank_get(head); +} + +static int +nvkm_disp_vblank_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nvkm_disp *disp = + container_of(notify->event, typeof(*disp), vblank); + union { + struct nvif_notify_head_req_v0 v0; + } *req = data; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) { + notify->size = sizeof(struct nvif_notify_head_rep_v0); + if (ret = -ENXIO, req->v0.head <= disp->vblank.index_nr) { + notify->types = 1; + notify->index = req->v0.head; + return 0; + } + } + + return ret; +} + +static const struct nvkm_event_func +nvkm_disp_vblank_func = { + .ctor = nvkm_disp_vblank_ctor, + .init = nvkm_disp_vblank_init, + .fini = nvkm_disp_vblank_fini, +}; + +void +nvkm_disp_vblank(struct nvkm_disp *disp, int head) +{ + struct nvif_notify_head_rep_v0 rep = {}; + nvkm_event_send(&disp->vblank, 1, head, &rep, sizeof(rep)); +} + +static int +nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nvkm_disp *disp = + container_of(notify->event, typeof(*disp), hpd); + union { + struct nvif_notify_conn_req_v0 v0; + } *req = data; + struct nvkm_outp *outp; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) { + notify->size = sizeof(struct nvif_notify_conn_rep_v0); + list_for_each_entry(outp, &disp->outps, head) { + if (ret = -ENXIO, outp->conn->index == req->v0.conn) { + if (ret = -ENODEV, outp->conn->hpd.event) { + notify->types = req->v0.mask; + notify->index = req->v0.conn; + ret = 0; + } + break; + } + } + } + + return ret; +} + +static const struct nvkm_event_func +nvkm_disp_hpd_func = { + .ctor = nvkm_disp_hpd_ctor +}; + +int +nvkm_disp_ntfy(struct nvkm_object *object, u32 type, struct nvkm_event **event) +{ + struct nvkm_disp *disp = nvkm_disp(object->engine); + switch (type) { + case NV04_DISP_NTFY_VBLANK: + *event = &disp->vblank; + return 0; + case NV04_DISP_NTFY_CONN: + *event = &disp->hpd; + return 0; + default: + break; + } + return -EINVAL; +} + +static int +nvkm_disp_class_new(struct nvkm_device *device, + const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + return nvkm_udisp_new(oclass, data, size, pobject); +} + +static const struct nvkm_device_oclass +nvkm_disp_sclass = { + .ctor = nvkm_disp_class_new, +}; + +static int +nvkm_disp_class_get(struct nvkm_oclass *oclass, int index, + const struct nvkm_device_oclass **class) +{ + struct nvkm_disp *disp = nvkm_disp(oclass->engine); + if (index == 0) { + oclass->base = disp->func->root; + *class = &nvkm_disp_sclass; + return 0; + } + return 1; +} + +static void +nvkm_disp_intr(struct nvkm_engine *engine) +{ + struct nvkm_disp *disp = nvkm_disp(engine); + disp->func->intr(disp); +} + +static int +nvkm_disp_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_disp *disp = nvkm_disp(engine); + struct nvkm_conn *conn; + struct nvkm_outp *outp; + + if (disp->func->fini) + disp->func->fini(disp); + + list_for_each_entry(outp, &disp->outps, head) { + nvkm_outp_fini(outp); + } + + list_for_each_entry(conn, &disp->conns, head) { + nvkm_conn_fini(conn); + } + + return 0; +} + +static int +nvkm_disp_init(struct nvkm_engine *engine) +{ + struct nvkm_disp *disp = nvkm_disp(engine); + struct nvkm_conn *conn; + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + list_for_each_entry(conn, &disp->conns, head) { + nvkm_conn_init(conn); + } + + list_for_each_entry(outp, &disp->outps, head) { + nvkm_outp_init(outp); + } + + if (disp->func->init) { + int ret = disp->func->init(disp); + if (ret) + return ret; + } + + /* Set 'normal' (ie. when it's attached to a head) state for + * each output resource to 'fully enabled'. + */ + list_for_each_entry(ior, &disp->iors, head) { + ior->func->power(ior, true, true, true, true, true); + } + + return 0; +} + +static int +nvkm_disp_oneinit(struct nvkm_engine *engine) +{ + struct nvkm_disp *disp = nvkm_disp(engine); + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvkm_outp *outp, *outt, *pair; + struct nvkm_conn *conn; + struct nvkm_head *head; + struct nvkm_ior *ior; + struct nvbios_connE connE; + struct dcb_output dcbE; + u8 hpd = 0, ver, hdr; + u32 data; + int ret, i; + + /* Create output path objects for each VBIOS display path. */ + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { + if (ver < 0x40) /* No support for chipsets prior to NV50. */ + break; + if (dcbE.type == DCB_OUTPUT_UNUSED) + continue; + if (dcbE.type == DCB_OUTPUT_EOL) + break; + outp = NULL; + + switch (dcbE.type) { + case DCB_OUTPUT_ANALOG: + case DCB_OUTPUT_TV: + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + ret = nvkm_outp_new(disp, i, &dcbE, &outp); + break; + case DCB_OUTPUT_DP: + ret = nvkm_dp_new(disp, i, &dcbE, &outp); + break; + case DCB_OUTPUT_WFD: + /* No support for WFD yet. */ + ret = -ENODEV; + continue; + default: + nvkm_warn(subdev, "dcb %d type %d unknown\n", + i, dcbE.type); + continue; + } + + if (ret) { + if (outp) { + if (ret != -ENODEV) + OUTP_ERR(outp, "ctor failed: %d", ret); + else + OUTP_DBG(outp, "not supported"); + nvkm_outp_del(&outp); + continue; + } + nvkm_error(subdev, "failed to create outp %d\n", i); + continue; + } + + list_add_tail(&outp->head, &disp->outps); + hpd = max(hpd, (u8)(dcbE.connector + 1)); + } + + /* Create connector objects based on available output paths. */ + list_for_each_entry_safe(outp, outt, &disp->outps, head) { + /* VBIOS data *should* give us the most useful information. */ + data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, + &connE); + + /* No bios connector data... */ + if (!data) { + /* Heuristic: anything with the same ccb index is + * considered to be on the same connector, any + * output path without an associated ccb entry will + * be put on its own connector. + */ + int ccb_index = outp->info.i2c_index; + if (ccb_index != 0xf) { + list_for_each_entry(pair, &disp->outps, head) { + if (pair->info.i2c_index == ccb_index) { + outp->conn = pair->conn; + break; + } + } + } + + /* Connector shared with another output path. */ + if (outp->conn) + continue; + + memset(&connE, 0x00, sizeof(connE)); + connE.type = DCB_CONNECTOR_NONE; + i = -1; + } else { + i = outp->info.connector; + } + + /* Check that we haven't already created this connector. */ + list_for_each_entry(conn, &disp->conns, head) { + if (conn->index == outp->info.connector) { + outp->conn = conn; + break; + } + } + + if (outp->conn) + continue; + + /* Apparently we need to create a new one! */ + ret = nvkm_conn_new(disp, i, &connE, &outp->conn); + if (ret) { + nvkm_error(&disp->engine.subdev, + "failed to create outp %d conn: %d\n", + outp->index, ret); + nvkm_conn_del(&outp->conn); + list_del(&outp->head); + nvkm_outp_del(&outp); + continue; + } + + list_add_tail(&outp->conn->head, &disp->conns); + } + + ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd); + if (ret) + return ret; + + if (disp->func->oneinit) { + ret = disp->func->oneinit(disp); + if (ret) + return ret; + } + + /* Enforce identity-mapped SOR assignment for panels, which have + * certain bits (ie. backlight controls) wired to a specific SOR. + */ + list_for_each_entry(outp, &disp->outps, head) { + if (outp->conn->info.type == DCB_CONNECTOR_LVDS || + outp->conn->info.type == DCB_CONNECTOR_eDP) { + ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1); + if (!WARN_ON(!ior)) + ior->identity = true; + outp->identity = true; + } + } + + i = 0; + list_for_each_entry(head, &disp->heads, head) + i = max(i, head->id + 1); + + return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank); +} + +static void * +nvkm_disp_dtor(struct nvkm_engine *engine) +{ + struct nvkm_disp *disp = nvkm_disp(engine); + struct nvkm_conn *conn; + struct nvkm_outp *outp; + struct nvkm_ior *ior; + struct nvkm_head *head; + void *data = disp; + + nvkm_ramht_del(&disp->ramht); + nvkm_gpuobj_del(&disp->inst); + + nvkm_event_fini(&disp->uevent); + + if (disp->super.wq) { + destroy_workqueue(disp->super.wq); + mutex_destroy(&disp->super.mutex); + } + + nvkm_event_fini(&disp->vblank); + nvkm_event_fini(&disp->hpd); + + while (!list_empty(&disp->conns)) { + conn = list_first_entry(&disp->conns, typeof(*conn), head); + list_del(&conn->head); + nvkm_conn_del(&conn); + } + + while (!list_empty(&disp->outps)) { + outp = list_first_entry(&disp->outps, typeof(*outp), head); + list_del(&outp->head); + nvkm_outp_del(&outp); + } + + while (!list_empty(&disp->iors)) { + ior = list_first_entry(&disp->iors, typeof(*ior), head); + nvkm_ior_del(&ior); + } + + while (!list_empty(&disp->heads)) { + head = list_first_entry(&disp->heads, typeof(*head), head); + nvkm_head_del(&head); + } + + return data; +} + +static const struct nvkm_engine_func +nvkm_disp = { + .dtor = nvkm_disp_dtor, + .oneinit = nvkm_disp_oneinit, + .init = nvkm_disp_init, + .fini = nvkm_disp_fini, + .intr = nvkm_disp_intr, + .base.sclass = nvkm_disp_class_get, +}; + +int +nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_disp **pdisp) +{ + struct nvkm_disp *disp; + int ret; + + if (!(disp = *pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL))) + return -ENOMEM; + + disp->func = func; + INIT_LIST_HEAD(&disp->heads); + INIT_LIST_HEAD(&disp->iors); + INIT_LIST_HEAD(&disp->outps); + INIT_LIST_HEAD(&disp->conns); + spin_lock_init(&disp->client.lock); + + ret = nvkm_engine_ctor(&nvkm_disp, device, type, inst, true, &disp->engine); + if (ret) + return ret; + + if (func->super) { + disp->super.wq = create_singlethread_workqueue("nvkm-disp"); + if (!disp->super.wq) + return -ENOMEM; + + INIT_WORK(&disp->super.work, func->super); + mutex_init(&disp->super.mutex); + } + + return nvkm_event_init(func->uevent, 1, ARRAY_SIZE(disp->chan), &disp->uevent); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.c new file mode 100644 index 000000000..d5e18daed --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.c @@ -0,0 +1,275 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "chan.h" + +#include +#include + +#include + +static int +nvkm_disp_chan_rd32(struct nvkm_object *object, u64 addr, u32 *data) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + struct nvkm_device *device = chan->disp->engine.subdev.device; + u64 size, base = chan->func->user(chan, &size); + + *data = nvkm_rd32(device, base + addr); + return 0; +} + +static int +nvkm_disp_chan_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + struct nvkm_device *device = chan->disp->engine.subdev.device; + u64 size, base = chan->func->user(chan, &size); + + nvkm_wr32(device, base + addr, data); + return 0; +} + +static int +nvkm_disp_chan_ntfy(struct nvkm_object *object, u32 type, struct nvkm_event **pevent) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + struct nvkm_disp *disp = chan->disp; + + switch (type) { + case 0: + *pevent = &disp->uevent; + return 0; + default: + break; + } + + return -EINVAL; +} + +static int +nvkm_disp_chan_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u64 base = device->func->resource_addr(device, 0); + + *type = NVKM_OBJECT_MAP_IO; + *addr = base + chan->func->user(chan, size); + return 0; +} + +struct nvkm_disp_chan_object { + struct nvkm_oproxy oproxy; + struct nvkm_disp *disp; + int hash; +}; + +static void +nvkm_disp_chan_child_del_(struct nvkm_oproxy *base) +{ + struct nvkm_disp_chan_object *object = container_of(base, typeof(*object), oproxy); + + nvkm_ramht_remove(object->disp->ramht, object->hash); +} + +static const struct nvkm_oproxy_func +nvkm_disp_chan_child_func_ = { + .dtor[0] = nvkm_disp_chan_child_del_, +}; + +static int +nvkm_disp_chan_child_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(oclass->parent); + struct nvkm_disp *disp = chan->disp; + struct nvkm_device *device = disp->engine.subdev.device; + const struct nvkm_device_oclass *sclass = oclass->priv; + struct nvkm_disp_chan_object *object; + int ret; + + if (!(object = kzalloc(sizeof(*object), GFP_KERNEL))) + return -ENOMEM; + nvkm_oproxy_ctor(&nvkm_disp_chan_child_func_, oclass, &object->oproxy); + object->disp = disp; + *pobject = &object->oproxy.base; + + ret = sclass->ctor(device, oclass, argv, argc, &object->oproxy.object); + if (ret) + return ret; + + object->hash = chan->func->bind(chan, object->oproxy.object, oclass->handle); + if (object->hash < 0) + return object->hash; + + return 0; +} + +static int +nvkm_disp_chan_child_get(struct nvkm_object *object, int index, struct nvkm_oclass *sclass) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + struct nvkm_device *device = chan->disp->engine.subdev.device; + const struct nvkm_device_oclass *oclass = NULL; + + if (chan->func->bind) + sclass->engine = nvkm_device_engine(device, NVKM_ENGINE_DMAOBJ, 0); + else + sclass->engine = NULL; + + if (sclass->engine && sclass->engine->func->base.sclass) { + sclass->engine->func->base.sclass(sclass, index, &oclass); + if (oclass) { + sclass->ctor = nvkm_disp_chan_child_new; + sclass->priv = oclass; + return 0; + } + } + + return -EINVAL; +} + +static int +nvkm_disp_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + + chan->func->fini(chan); + chan->func->intr(chan, false); + return 0; +} + +static int +nvkm_disp_chan_init(struct nvkm_object *object) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + + chan->func->intr(chan, true); + return chan->func->init(chan); +} + +static void * +nvkm_disp_chan_dtor(struct nvkm_object *object) +{ + struct nvkm_disp_chan *chan = nvkm_disp_chan(object); + struct nvkm_disp *disp = chan->disp; + + spin_lock(&disp->client.lock); + if (disp->chan[chan->chid.user] == chan) + disp->chan[chan->chid.user] = NULL; + spin_unlock(&disp->client.lock); + + nvkm_memory_unref(&chan->memory); + return chan; +} + +static const struct nvkm_object_func +nvkm_disp_chan = { + .dtor = nvkm_disp_chan_dtor, + .init = nvkm_disp_chan_init, + .fini = nvkm_disp_chan_fini, + .rd32 = nvkm_disp_chan_rd32, + .wr32 = nvkm_disp_chan_wr32, + .ntfy = nvkm_disp_chan_ntfy, + .map = nvkm_disp_chan_map, + .sclass = nvkm_disp_chan_child_get, +}; + +static int +nvkm_disp_chan_new_(struct nvkm_disp *disp, int nr, const struct nvkm_oclass *oclass, + void *argv, u32 argc, struct nvkm_object **pobject) +{ + const struct nvkm_disp_chan_user *user = NULL; + struct nvkm_disp_chan *chan; + union nvif_disp_chan_args *args = argv; + int ret, i; + + for (i = 0; disp->func->user[i].ctor; i++) { + if (disp->func->user[i].base.oclass == oclass->base.oclass) { + user = disp->func->user[i].chan; + break; + } + } + + if (WARN_ON(!user)) + return -EINVAL; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (args->v0.id >= nr || !args->v0.pushbuf != !user->func->push) + return -EINVAL; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->object; + + nvkm_object_ctor(&nvkm_disp_chan, oclass, &chan->object); + chan->func = user->func; + chan->mthd = user->mthd; + chan->disp = disp; + chan->chid.ctrl = user->ctrl + args->v0.id; + chan->chid.user = user->user + args->v0.id; + chan->head = args->v0.id; + + if (chan->func->push) { + ret = chan->func->push(chan, args->v0.pushbuf); + if (ret) + return ret; + } + + spin_lock(&disp->client.lock); + if (disp->chan[chan->chid.user]) { + spin_unlock(&disp->client.lock); + return -EBUSY; + } + disp->chan[chan->chid.user] = chan; + spin_unlock(&disp->client.lock); + return 0; +} + +int +nvkm_disp_wndw_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = nvkm_udisp(oclass->parent); + + return nvkm_disp_chan_new_(disp, disp->wndw.nr, oclass, argv, argc, pobject); +} + +int +nvkm_disp_chan_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = nvkm_udisp(oclass->parent); + + return nvkm_disp_chan_new_(disp, disp->head.nr, oclass, argv, argc, pobject); +} + +int +nvkm_disp_core_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = nvkm_udisp(oclass->parent); + + return nvkm_disp_chan_new_(disp, 1, oclass, argv, argc, pobject); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.h new file mode 100644 index 000000000..398336ffb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_CHAN_H__ +#define __NVKM_DISP_CHAN_H__ +#define nvkm_disp_chan(p) container_of((p), struct nvkm_disp_chan, object) +#include +#include "priv.h" + +struct nvkm_disp_chan { + const struct nvkm_disp_chan_func *func; + const struct nvkm_disp_chan_mthd *mthd; + struct nvkm_disp *disp; + + struct { + int ctrl; + int user; + } chid; + int head; + + struct nvkm_object object; + + struct nvkm_memory *memory; + u64 push; + + u32 suspend_put; +}; + +int nvkm_disp_core_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); +int nvkm_disp_chan_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); +int nvkm_disp_wndw_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); + +struct nvkm_disp_chan_func { + int (*push)(struct nvkm_disp_chan *, u64 object); + int (*init)(struct nvkm_disp_chan *); + void (*fini)(struct nvkm_disp_chan *); + void (*intr)(struct nvkm_disp_chan *, bool en); + u64 (*user)(struct nvkm_disp_chan *, u64 *size); + int (*bind)(struct nvkm_disp_chan *, struct nvkm_object *, u32 handle); +}; + +void nv50_disp_chan_intr(struct nvkm_disp_chan *, bool); +u64 nv50_disp_chan_user(struct nvkm_disp_chan *, u64 *); +extern const struct nvkm_disp_chan_func nv50_disp_pioc_func; +extern const struct nvkm_disp_chan_func nv50_disp_dmac_func; +int nv50_disp_dmac_push(struct nvkm_disp_chan *, u64); +int nv50_disp_dmac_bind(struct nvkm_disp_chan *, struct nvkm_object *, u32); +extern const struct nvkm_disp_chan_func nv50_disp_core_func; + +void gf119_disp_chan_intr(struct nvkm_disp_chan *, bool); +extern const struct nvkm_disp_chan_func gf119_disp_pioc_func; +extern const struct nvkm_disp_chan_func gf119_disp_dmac_func; +void gf119_disp_dmac_fini(struct nvkm_disp_chan *); +int gf119_disp_dmac_bind(struct nvkm_disp_chan *, struct nvkm_object *, u32); +extern const struct nvkm_disp_chan_func gf119_disp_core_func; +void gf119_disp_core_fini(struct nvkm_disp_chan *); + +extern const struct nvkm_disp_chan_func gp102_disp_dmac_func; + +u64 gv100_disp_chan_user(struct nvkm_disp_chan *, u64 *); +int gv100_disp_dmac_init(struct nvkm_disp_chan *); +void gv100_disp_dmac_fini(struct nvkm_disp_chan *); +int gv100_disp_dmac_bind(struct nvkm_disp_chan *, struct nvkm_object *, u32); + +struct nvkm_disp_chan_user { + const struct nvkm_disp_chan_func *func; + int ctrl; + int user; + const struct nvkm_disp_chan_mthd *mthd; +}; + +extern const struct nvkm_disp_chan_user nv50_disp_oimm; +extern const struct nvkm_disp_chan_user nv50_disp_curs; + +extern const struct nvkm_disp_chan_user g84_disp_core; +extern const struct nvkm_disp_chan_user g84_disp_base; +extern const struct nvkm_disp_chan_user g84_disp_ovly; + +extern const struct nvkm_disp_chan_user g94_disp_core; + +extern const struct nvkm_disp_chan_user gt200_disp_ovly; + +extern const struct nvkm_disp_chan_user gf119_disp_base; +extern const struct nvkm_disp_chan_user gf119_disp_oimm; +extern const struct nvkm_disp_chan_user gf119_disp_curs; + +extern const struct nvkm_disp_chan_user gk104_disp_core; +extern const struct nvkm_disp_chan_user gk104_disp_ovly; + +extern const struct nvkm_disp_chan_user gv100_disp_core; +extern const struct nvkm_disp_chan_user gv100_disp_curs; +extern const struct nvkm_disp_chan_user gv100_disp_wndw; +extern const struct nvkm_disp_chan_user gv100_disp_wimm; + +struct nvkm_disp_mthd_list { + u32 mthd; + u32 addr; + struct { + u32 mthd; + u32 addr; + const char *name; + } data[]; +}; + +struct nvkm_disp_chan_mthd { + const char *name; + u32 addr; + s32 prev; + struct { + const char *name; + int nr; + const struct nvkm_disp_mthd_list *mthd; + } data[]; +}; + +void nv50_disp_chan_mthd(struct nvkm_disp_chan *, int debug); + +extern const struct nvkm_disp_mthd_list nv50_disp_core_mthd_base; +extern const struct nvkm_disp_mthd_list nv50_disp_core_mthd_sor; +extern const struct nvkm_disp_mthd_list nv50_disp_core_mthd_pior; +extern const struct nvkm_disp_mthd_list nv50_disp_base_mthd_image; + +extern const struct nvkm_disp_chan_mthd g84_disp_core_mthd; +extern const struct nvkm_disp_mthd_list g84_disp_core_mthd_dac; +extern const struct nvkm_disp_mthd_list g84_disp_core_mthd_head; + +extern const struct nvkm_disp_chan_mthd g94_disp_core_mthd; + +extern const struct nvkm_disp_mthd_list gf119_disp_core_mthd_base; +extern const struct nvkm_disp_mthd_list gf119_disp_core_mthd_dac; +extern const struct nvkm_disp_mthd_list gf119_disp_core_mthd_sor; +extern const struct nvkm_disp_mthd_list gf119_disp_core_mthd_pior; +extern const struct nvkm_disp_chan_mthd gf119_disp_base_mthd; + +extern const struct nvkm_disp_chan_mthd gk104_disp_core_mthd; +extern const struct nvkm_disp_chan_mthd gk104_disp_ovly_mthd; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c new file mode 100644 index 000000000..7ed11801a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c @@ -0,0 +1,134 @@ +/* + * Copyright 2014 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 "conn.h" +#include "outp.h" +#include "priv.h" + +#include + +#include + +static int +nvkm_conn_hpd(struct nvkm_notify *notify) +{ + struct nvkm_conn *conn = container_of(notify, typeof(*conn), hpd); + struct nvkm_disp *disp = conn->disp; + struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; + const struct nvkm_gpio_ntfy_rep *line = notify->data; + struct nvif_notify_conn_rep_v0 rep; + int index = conn->index; + + CONN_DBG(conn, "HPD: %d", line->mask); + + if (!nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.index)) + rep.mask = NVIF_NOTIFY_CONN_V0_UNPLUG; + else + rep.mask = NVIF_NOTIFY_CONN_V0_PLUG; + rep.version = 0; + + nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep)); + return NVKM_NOTIFY_KEEP; +} + +void +nvkm_conn_fini(struct nvkm_conn *conn) +{ + nvkm_notify_put(&conn->hpd); +} + +void +nvkm_conn_init(struct nvkm_conn *conn) +{ + nvkm_notify_get(&conn->hpd); +} + +void +nvkm_conn_del(struct nvkm_conn **pconn) +{ + struct nvkm_conn *conn = *pconn; + if (conn) { + nvkm_notify_fini(&conn->hpd); + kfree(*pconn); + *pconn = NULL; + } +} + +static void +nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info, + struct nvkm_conn *conn) +{ + static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 }; + struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; + struct dcb_gpio_func func; + int ret; + + conn->disp = disp; + conn->index = index; + conn->info = *info; + conn->info.hpd = DCB_GPIO_UNUSED; + + CONN_DBG(conn, "type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x", + info->type, info->location, info->hpd, info->dp, + info->di, info->sr, info->lcdid); + + if ((info->hpd = ffs(info->hpd))) { + if (--info->hpd >= ARRAY_SIZE(hpd)) { + CONN_ERR(conn, "hpd %02x unknown", info->hpd); + return; + } + info->hpd = hpd[info->hpd]; + + ret = nvkm_gpio_find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func); + if (ret) { + CONN_ERR(conn, "func %02x lookup failed, %d", info->hpd, ret); + return; + } + + conn->info.hpd = func.line; + + ret = nvkm_notify_init(NULL, &gpio->event, nvkm_conn_hpd, + true, &(struct nvkm_gpio_ntfy_req) { + .mask = NVKM_GPIO_TOGGLED, + .line = func.line, + }, + sizeof(struct nvkm_gpio_ntfy_req), + sizeof(struct nvkm_gpio_ntfy_rep), + &conn->hpd); + if (ret) { + CONN_ERR(conn, "func %02x failed, %d", info->hpd, ret); + } else { + CONN_DBG(conn, "func %02x (HPD)", info->hpd); + } + } +} + +int +nvkm_conn_new(struct nvkm_disp *disp, int index, struct nvbios_connE *info, + struct nvkm_conn **pconn) +{ + if (!(*pconn = kzalloc(sizeof(**pconn), GFP_KERNEL))) + return -ENOMEM; + nvkm_conn_ctor(disp, index, info, *pconn); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h new file mode 100644 index 000000000..f109634ce --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_CONN_H__ +#define __NVKM_DISP_CONN_H__ +#include "priv.h" + +#include +#include +#include + +struct nvkm_conn { + struct nvkm_disp *disp; + int index; + struct nvbios_connE info; + + struct nvkm_notify hpd; + + struct list_head head; + + struct nvkm_object object; +}; + +int nvkm_conn_new(struct nvkm_disp *, int index, struct nvbios_connE *, + struct nvkm_conn **); +void nvkm_conn_del(struct nvkm_conn **); +void nvkm_conn_init(struct nvkm_conn *); +void nvkm_conn_fini(struct nvkm_conn *); + +#define CONN_MSG(c,l,f,a...) do { \ + struct nvkm_conn *_conn = (c); \ + nvkm_##l(&_conn->disp->engine.subdev, "conn %02x:%02x%02x: "f"\n", \ + _conn->index, _conn->info.location, _conn->info.type, ##a); \ +} while(0) +#define CONN_ERR(c,f,a...) CONN_MSG((c), error, f, ##a) +#define CONN_DBG(c,f,a...) CONN_MSG((c), debug, f, ##a) +#define CONN_TRACE(c,f,a...) CONN_MSG((c), trace, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c new file mode 100644 index 000000000..458f8efb1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -0,0 +1,864 @@ +/* + * Copyright 2014 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 "dp.h" +#include "conn.h" +#include "head.h" +#include "ior.h" + +#include + +#include +#include +#include +#include + +#include + +/* IED scripts are no longer used by UEFI/RM from Ampere, but have been updated for + * the x86 option ROM. However, the relevant VBIOS table versions weren't modified, + * so we're unable to detect this in a nice way. + */ +#define AMPERE_IED_HACK(disp) ((disp)->engine.subdev.device->card_type >= GA100) + +struct lt_state { + struct nvkm_outp *outp; + + int repeaters; + int repeater; + + u8 stat[6]; + u8 conf[4]; + bool pc2; + u8 pc2stat; + u8 pc2conf[2]; +}; + +static int +nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay) +{ + struct nvkm_outp *outp = lt->outp; + u32 addr; + int ret; + + usleep_range(delay, delay * 2); + + if (lt->repeater) + addr = DPCD_LTTPR_LANE0_1_STATUS(lt->repeater); + else + addr = DPCD_LS02; + + ret = nvkm_rdaux(outp->dp.aux, addr, <->stat[0], 3); + if (ret) + return ret; + + if (lt->repeater) + addr = DPCD_LTTPR_LANE0_1_ADJUST(lt->repeater); + else + addr = DPCD_LS06; + + ret = nvkm_rdaux(outp->dp.aux, addr, <->stat[4], 2); + if (ret) + return ret; + + if (pc) { + ret = nvkm_rdaux(outp->dp.aux, DPCD_LS0C, <->pc2stat, 1); + if (ret) + lt->pc2stat = 0x00; + + OUTP_TRACE(outp, "status %6ph pc2 %02x", lt->stat, lt->pc2stat); + } else { + OUTP_TRACE(outp, "status %6ph", lt->stat); + } + + return 0; +} + +static int +nvkm_dp_train_drive(struct lt_state *lt, bool pc) +{ + struct nvkm_outp *outp = lt->outp; + struct nvkm_ior *ior = outp->ior; + struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios; + struct nvbios_dpout info; + struct nvbios_dpcfg ocfg; + u8 ver, hdr, cnt, len; + u32 addr; + u32 data; + int ret, i; + + for (i = 0; i < ior->dp.nr; i++) { + u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3; + u8 lpre = (lane & 0x0c) >> 2; + u8 lvsw = (lane & 0x03) >> 0; + u8 hivs = 3 - lpre; + u8 hipe = 3; + u8 hipc = 3; + + if (lpc2 >= hipc) + lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; + if (lpre >= hipe) { + lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ + lvsw = hivs = 3 - (lpre & 3); + } else + if (lvsw >= hivs) { + lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; + } + + lt->conf[i] = (lpre << 3) | lvsw; + lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); + + OUTP_TRACE(outp, "config lane %d %02x %02x", i, lt->conf[i], lpc2); + + if (lt->repeater != lt->repeaters) + continue; + + data = nvbios_dpout_match(bios, outp->info.hasht, outp->info.hashm, + &ver, &hdr, &cnt, &len, &info); + if (!data) + continue; + + data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3, lpre & 3, + &ver, &hdr, &cnt, &len, &ocfg); + if (!data) + continue; + + ior->func->dp->drive(ior, i, ocfg.pc, ocfg.dc, ocfg.pe, ocfg.tx_pu); + } + + if (lt->repeater) + addr = DPCD_LTTPR_LANE0_SET(lt->repeater); + else + addr = DPCD_LC03(0); + + ret = nvkm_wraux(outp->dp.aux, addr, lt->conf, 4); + if (ret) + return ret; + + if (pc) { + ret = nvkm_wraux(outp->dp.aux, DPCD_LC0F, lt->pc2conf, 2); + if (ret) + return ret; + } + + return 0; +} + +static void +nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern) +{ + struct nvkm_outp *outp = lt->outp; + u32 addr; + u8 sink_tp; + + OUTP_TRACE(outp, "training pattern %d", pattern); + outp->ior->func->dp->pattern(outp->ior, pattern); + + if (lt->repeater) + addr = DPCD_LTTPR_PATTERN_SET(lt->repeater); + else + addr = DPCD_LC02; + + nvkm_rdaux(outp->dp.aux, addr, &sink_tp, 1); + sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; + sink_tp |= (pattern != 4) ? pattern : 7; + + if (pattern != 0) + sink_tp |= DPCD_LC02_SCRAMBLING_DISABLE; + else + sink_tp &= ~DPCD_LC02_SCRAMBLING_DISABLE; + nvkm_wraux(outp->dp.aux, addr, &sink_tp, 1); +} + +static int +nvkm_dp_train_eq(struct lt_state *lt) +{ + struct nvkm_i2c_aux *aux = lt->outp->dp.aux; + bool eq_done = false, cr_done = true; + int tries = 0, usec = 0, i; + u8 data; + + if (lt->repeater) { + if (!nvkm_rdaux(aux, DPCD_LTTPR_AUX_RD_INTERVAL(lt->repeater), &data, sizeof(data))) + usec = (data & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; + + nvkm_dp_train_pattern(lt, 4); + } else { + if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] >= 0x14 && + lt->outp->dp.dpcd[DPCD_RC03] & DPCD_RC03_TPS4_SUPPORTED) + nvkm_dp_train_pattern(lt, 4); + else + if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] >= 0x12 && + lt->outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) + nvkm_dp_train_pattern(lt, 3); + else + nvkm_dp_train_pattern(lt, 2); + + usec = (lt->outp->dp.dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; + } + + do { + if ((tries && + nvkm_dp_train_drive(lt, lt->pc2)) || + nvkm_dp_train_sense(lt, lt->pc2, usec ? usec : 400)) + break; + + eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); + for (i = 0; i < lt->outp->ior->dp.nr && eq_done; i++) { + u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) + cr_done = false; + if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) + eq_done = false; + } + } while (!eq_done && cr_done && ++tries <= 5); + + return eq_done ? 0 : -1; +} + +static int +nvkm_dp_train_cr(struct lt_state *lt) +{ + bool cr_done = false, abort = false; + int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + int tries = 0, usec = 0, i; + + nvkm_dp_train_pattern(lt, 1); + + if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x14 && !lt->repeater) + usec = (lt->outp->dp.dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; + + do { + if (nvkm_dp_train_drive(lt, false) || + nvkm_dp_train_sense(lt, false, usec ? usec : 100)) + break; + + cr_done = true; + for (i = 0; i < lt->outp->ior->dp.nr; i++) { + u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { + cr_done = false; + if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED) + abort = true; + break; + } + } + + if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { + voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + tries = 0; + } + } while (!cr_done && !abort && ++tries < 5); + + return cr_done ? 0 : -1; +} + +static int +nvkm_dp_train_links(struct nvkm_outp *outp, int rate) +{ + struct nvkm_ior *ior = outp->ior; + struct nvkm_disp *disp = outp->disp; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct lt_state lt = { + .outp = outp, + }; + u32 lnkcmp; + u8 sink[2], data; + int ret; + + OUTP_DBG(outp, "training %d x %d MB/s", ior->dp.nr, ior->dp.bw * 27); + + /* Intersect misc. capabilities of the OR and sink. */ + if (disp->engine.subdev.device->chipset < 0x110) + outp->dp.dpcd[DPCD_RC03] &= ~DPCD_RC03_TPS4_SUPPORTED; + if (disp->engine.subdev.device->chipset < 0xd0) + outp->dp.dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED; + lt.pc2 = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED; + + if (AMPERE_IED_HACK(disp) && (lnkcmp = lt.outp->dp.info.script[0])) { + /* Execute BeforeLinkTraining script from DP Info table. */ + while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) + lnkcmp += 3; + lnkcmp = nvbios_rd16(bios, lnkcmp + 1); + + nvbios_init(&outp->disp->engine.subdev, lnkcmp, + init.outp = &outp->info; + init.or = ior->id; + init.link = ior->asy.link; + ); + } + + /* Set desired link configuration on the source. */ + if ((lnkcmp = lt.outp->dp.info.lnkcmp)) { + if (outp->dp.version < 0x30) { + while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp)) + lnkcmp += 4; + lnkcmp = nvbios_rd16(bios, lnkcmp + 2); + } else { + while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) + lnkcmp += 3; + lnkcmp = nvbios_rd16(bios, lnkcmp + 1); + } + + nvbios_init(subdev, lnkcmp, + init.outp = &outp->info; + init.or = ior->id; + init.link = ior->asy.link; + ); + } + + ret = ior->func->dp->links(ior, outp->dp.aux); + if (ret) { + if (ret < 0) { + OUTP_ERR(outp, "train failed with %d", ret); + return ret; + } + return 0; + } + + ior->func->dp->power(ior, ior->dp.nr); + + /* Select LTTPR non-transparent mode if we have a valid configuration, + * use transparent mode otherwise. + */ + if (outp->dp.lttpr[0] >= 0x14) { + data = DPCD_LTTPR_MODE_TRANSPARENT; + nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); + + if (outp->dp.lttprs) { + data = DPCD_LTTPR_MODE_NON_TRANSPARENT; + nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); + lt.repeaters = outp->dp.lttprs; + } + } + + /* Set desired link configuration on the sink. */ + sink[0] = (outp->dp.rate[rate].dpcd < 0) ? ior->dp.bw : 0; + sink[1] = ior->dp.nr; + if (ior->dp.ef) + sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; + + ret = nvkm_wraux(outp->dp.aux, DPCD_LC00_LINK_BW_SET, sink, 2); + if (ret) + return ret; + + if (outp->dp.rate[rate].dpcd >= 0) { + ret = nvkm_rdaux(outp->dp.aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); + if (ret) + return ret; + + sink[0] &= ~DPCD_LC15_LINK_RATE_SET_MASK; + sink[0] |= outp->dp.rate[rate].dpcd; + + ret = nvkm_wraux(outp->dp.aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); + if (ret) + return ret; + } + + /* Attempt to train the link in this configuration. */ + for (lt.repeater = lt.repeaters; lt.repeater >= 0; lt.repeater--) { + if (lt.repeater) + OUTP_DBG(outp, "training LTTPR%d", lt.repeater); + else + OUTP_DBG(outp, "training sink"); + + memset(lt.stat, 0x00, sizeof(lt.stat)); + ret = nvkm_dp_train_cr(<); + if (ret == 0) + ret = nvkm_dp_train_eq(<); + nvkm_dp_train_pattern(<, 0); + } + + return ret; +} + +static void +nvkm_dp_train_fini(struct nvkm_outp *outp) +{ + /* Execute AfterLinkTraining script from DP Info table. */ + nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[1], + init.outp = &outp->info; + init.or = outp->ior->id; + init.link = outp->ior->asy.link; + ); +} + +static void +nvkm_dp_train_init(struct nvkm_outp *outp) +{ + /* Execute EnableSpread/DisableSpread script from DP Info table. */ + if (outp->dp.dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) { + nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[2], + init.outp = &outp->info; + init.or = outp->ior->id; + init.link = outp->ior->asy.link; + ); + } else { + nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[3], + init.outp = &outp->info; + init.or = outp->ior->id; + init.link = outp->ior->asy.link; + ); + } + + if (!AMPERE_IED_HACK(outp->disp)) { + /* Execute BeforeLinkTraining script from DP Info table. */ + nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[0], + init.outp = &outp->info; + init.or = outp->ior->id; + init.link = outp->ior->asy.link; + ); + } +} + +static int +nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps) +{ + struct nvkm_ior *ior = outp->ior; + int ret = -EINVAL, nr, rate; + u8 pwr; + + /* Ensure sink is not in a low-power state. */ + if (!nvkm_rdaux(outp->dp.aux, DPCD_SC00, &pwr, 1)) { + if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { + pwr &= ~DPCD_SC00_SET_POWER; + pwr |= DPCD_SC00_SET_POWER_D0; + nvkm_wraux(outp->dp.aux, DPCD_SC00, &pwr, 1); + } + } + + ior->dp.mst = outp->dp.lt.mst; + ior->dp.ef = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; + ior->dp.nr = 0; + + /* Link training. */ + OUTP_DBG(outp, "training"); + nvkm_dp_train_init(outp); + for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) { + for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) { + if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) { + /* Program selected link configuration. */ + ior->dp.bw = outp->dp.rate[rate].rate / 27000; + ior->dp.nr = nr; + ret = nvkm_dp_train_links(outp, rate); + } + } + } + nvkm_dp_train_fini(outp); + if (ret < 0) + OUTP_ERR(outp, "training failed"); + else + OUTP_DBG(outp, "training done"); + atomic_set(&outp->dp.lt.done, 1); + return ret; +} + +/* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps() + * converted to work inside nvkm. This is a temporary holdover until we start + * passing the drm_dp_aux device through NVKM + */ +static int +nvkm_dp_read_dpcd_caps(struct nvkm_outp *outp) +{ + struct nvkm_i2c_aux *aux = outp->dp.aux; + u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; + int ret; + + ret = nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, DP_RECEIVER_CAP_SIZE); + if (ret < 0) + return ret; + + /* + * Prior to DP1.3 the bit represented by + * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. + * If it is set DP_DPCD_REV at 0000h could be at a value less than + * the true capability of the panel. The only way to check is to + * then compare 0000h and 2200h. + */ + if (!(outp->dp.dpcd[DP_TRAINING_AUX_RD_INTERVAL] & + DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) + return 0; + + ret = nvkm_rdaux(aux, DP_DP13_DPCD_REV, dpcd_ext, sizeof(dpcd_ext)); + if (ret < 0) + return ret; + + if (outp->dp.dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { + OUTP_DBG(outp, "Extended DPCD rev less than base DPCD rev (%d > %d)\n", + outp->dp.dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]); + return 0; + } + + if (!memcmp(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext))) + return 0; + + memcpy(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext)); + + return 0; +} + +void +nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) +{ + /* Execute DisableLT script from DP Info Table. */ + nvbios_init(&ior->disp->engine.subdev, outp->dp.info.script[4], + init.outp = &outp->info; + init.or = ior->id; + init.link = ior->arm.link; + ); +} + +static void +nvkm_dp_release(struct nvkm_outp *outp) +{ + /* Prevent link from being retrained if sink sends an IRQ. */ + atomic_set(&outp->dp.lt.done, 0); + outp->ior->dp.nr = 0; +} + +static int +nvkm_dp_acquire(struct nvkm_outp *outp) +{ + struct nvkm_ior *ior = outp->ior; + struct nvkm_head *head; + bool retrain = true; + u32 datakbps = 0; + u32 dataKBps; + u32 linkKBps; + u8 stat[3]; + int ret, i; + + mutex_lock(&outp->dp.mutex); + + /* Check that link configuration meets current requirements. */ + list_for_each_entry(head, &outp->disp->heads, head) { + if (ior->asy.head & (1 << head->id)) { + u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000; + datakbps += khz * head->asy.or.depth; + } + } + + linkKBps = ior->dp.bw * 27000 * ior->dp.nr; + dataKBps = DIV_ROUND_UP(datakbps, 8); + OUTP_DBG(outp, "data %d KB/s link %d KB/s mst %d->%d", + dataKBps, linkKBps, ior->dp.mst, outp->dp.lt.mst); + if (linkKBps < dataKBps || ior->dp.mst != outp->dp.lt.mst) { + OUTP_DBG(outp, "link requirements changed"); + goto done; + } + + /* Check that link is still trained. */ + ret = nvkm_rdaux(outp->dp.aux, DPCD_LS02, stat, 3); + if (ret) { + OUTP_DBG(outp, "failed to read link status, assuming no sink"); + goto done; + } + + if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { + for (i = 0; i < ior->dp.nr; i++) { + u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; + if (!(lane & DPCD_LS02_LANE0_CR_DONE) || + !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { + OUTP_DBG(outp, "lane %d not equalised", lane); + goto done; + } + } + retrain = false; + } else { + OUTP_DBG(outp, "no inter-lane alignment"); + } + +done: + if (retrain || !atomic_read(&outp->dp.lt.done)) + ret = nvkm_dp_train(outp, dataKBps); + mutex_unlock(&outp->dp.mutex); + return ret; +} + +static bool +nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp) +{ + u8 sink_rates[DPCD_RC10_SUPPORTED_LINK_RATES__SIZE]; + int i, j, k; + + if (outp->conn->info.type != DCB_CONNECTOR_eDP || + outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x13 || + nvkm_rdaux(outp->dp.aux, DPCD_RC10_SUPPORTED_LINK_RATES(0), + sink_rates, sizeof(sink_rates))) + return false; + + for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { + const u32 rate = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; + + if (!rate || WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) + break; + + if (rate > outp->info.dpconf.link_bw * 27000) { + OUTP_DBG(outp, "rate %d !outp", rate); + continue; + } + + for (j = 0; j < outp->dp.rates; j++) { + if (rate > outp->dp.rate[j].rate) { + for (k = outp->dp.rates; k > j; k--) + outp->dp.rate[k] = outp->dp.rate[k - 1]; + break; + } + } + + outp->dp.rate[j].dpcd = i / 2; + outp->dp.rate[j].rate = rate; + outp->dp.rates++; + } + + for (i = 0; i < outp->dp.rates; i++) + OUTP_DBG(outp, "link_rate[%d] = %d", outp->dp.rate[i].dpcd, outp->dp.rate[i].rate); + + return outp->dp.rates != 0; +} + +static bool +nvkm_dp_enable(struct nvkm_outp *outp, bool enable) +{ + struct nvkm_i2c_aux *aux = outp->dp.aux; + + if (enable) { + if (!outp->dp.present) { + OUTP_DBG(outp, "aux power -> always"); + nvkm_i2c_aux_monitor(aux, true); + outp->dp.present = true; + } + + /* Detect any LTTPRs before reading DPCD receiver caps. */ + if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) && + outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) { + switch (outp->dp.lttpr[2]) { + case 0x80: outp->dp.lttprs = 1; break; + case 0x40: outp->dp.lttprs = 2; break; + case 0x20: outp->dp.lttprs = 3; break; + case 0x10: outp->dp.lttprs = 4; break; + case 0x08: outp->dp.lttprs = 5; break; + case 0x04: outp->dp.lttprs = 6; break; + case 0x02: outp->dp.lttprs = 7; break; + case 0x01: outp->dp.lttprs = 8; break; + default: + /* Unknown LTTPR count, we'll switch to transparent mode. */ + WARN_ON(1); + outp->dp.lttprs = 0; + break; + } + } else { + /* No LTTPR support, or zero LTTPR count - don't touch it at all. */ + memset(outp->dp.lttpr, 0x00, sizeof(outp->dp.lttpr)); + } + + if (!nvkm_dp_read_dpcd_caps(outp)) { + const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 }; + const u8 *rate; + int rate_max; + + outp->dp.rates = 0; + outp->dp.links = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; + outp->dp.links = min(outp->dp.links, outp->info.dpconf.link_nr); + if (outp->dp.lttprs && outp->dp.lttpr[4]) + outp->dp.links = min_t(int, outp->dp.links, outp->dp.lttpr[4]); + + rate_max = outp->dp.dpcd[DPCD_RC01_MAX_LINK_RATE]; + rate_max = min(rate_max, outp->info.dpconf.link_bw); + if (outp->dp.lttprs && outp->dp.lttpr[1]) + rate_max = min_t(int, rate_max, outp->dp.lttpr[1]); + + if (!nvkm_dp_enable_supported_link_rates(outp)) { + for (rate = rates; *rate; rate++) { + if (*rate > rate_max) + continue; + + if (WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) + break; + + outp->dp.rate[outp->dp.rates].dpcd = -1; + outp->dp.rate[outp->dp.rates].rate = *rate * 27000; + outp->dp.rates++; + } + } + + return true; + } + } + + if (outp->dp.present) { + OUTP_DBG(outp, "aux power -> demand"); + nvkm_i2c_aux_monitor(aux, false); + outp->dp.present = false; + } + + atomic_set(&outp->dp.lt.done, 0); + return false; +} + +static int +nvkm_dp_hpd(struct nvkm_notify *notify) +{ + const struct nvkm_i2c_ntfy_rep *line = notify->data; + struct nvkm_outp *outp = container_of(notify, typeof(*outp), dp.hpd); + struct nvkm_conn *conn = outp->conn; + struct nvkm_disp *disp = outp->disp; + struct nvif_notify_conn_rep_v0 rep = {}; + + OUTP_DBG(outp, "HPD: %d", line->mask); + if (line->mask & NVKM_I2C_IRQ) { + if (atomic_read(&outp->dp.lt.done)) + outp->func->acquire(outp); + rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ; + } else { + nvkm_dp_enable(outp, true); + } + + if (line->mask & NVKM_I2C_UNPLUG) + rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG; + if (line->mask & NVKM_I2C_PLUG) + rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG; + + nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); + return NVKM_NOTIFY_KEEP; +} + +static void +nvkm_dp_fini(struct nvkm_outp *outp) +{ + nvkm_notify_put(&outp->dp.hpd); + nvkm_dp_enable(outp, false); +} + +static void +nvkm_dp_init(struct nvkm_outp *outp) +{ + struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; + + nvkm_notify_put(&outp->conn->hpd); + + /* eDP panels need powering on by us (if the VBIOS doesn't default it + * to on) before doing any AUX channel transactions. LVDS panel power + * is handled by the SOR itself, and not required for LVDS DDC. + */ + if (outp->conn->info.type == DCB_CONNECTOR_eDP) { + int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); + if (power == 0) + nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); + + /* We delay here unconditionally, even if already powered, + * because some laptop panels having a significant resume + * delay before the panel begins responding. + * + * This is likely a bit of a hack, but no better idea for + * handling this at the moment. + */ + msleep(300); + + /* If the eDP panel can't be detected, we need to restore + * the panel power GPIO to avoid breaking another output. + */ + if (!nvkm_dp_enable(outp, true) && power == 0) + nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0); + } else { + nvkm_dp_enable(outp, true); + } + + nvkm_notify_get(&outp->dp.hpd); +} + +static void * +nvkm_dp_dtor(struct nvkm_outp *outp) +{ + nvkm_notify_fini(&outp->dp.hpd); + return outp; +} + +static const struct nvkm_outp_func +nvkm_dp_func = { + .dtor = nvkm_dp_dtor, + .init = nvkm_dp_init, + .fini = nvkm_dp_fini, + .acquire = nvkm_dp_acquire, + .release = nvkm_dp_release, + .disable = nvkm_dp_disable, +}; + +int +nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct nvkm_outp **poutp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvkm_i2c *i2c = device->i2c; + struct nvkm_outp *outp; + u8 hdr, cnt, len; + u32 data; + int ret; + + ret = nvkm_outp_new_(&nvkm_dp_func, disp, index, dcbE, poutp); + outp = *poutp; + if (ret) + return ret; + + if (dcbE->location == 0) + outp->dp.aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index)); + else + outp->dp.aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); + if (!outp->dp.aux) { + OUTP_ERR(outp, "no aux"); + return -EINVAL; + } + + /* bios data is not optional */ + data = nvbios_dpout_match(bios, outp->info.hasht, outp->info.hashm, + &outp->dp.version, &hdr, &cnt, &len, &outp->dp.info); + if (!data) { + OUTP_ERR(outp, "no bios dp data"); + return -EINVAL; + } + + OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len); + + /* hotplug detect, replaces gpio-based mechanism with aux events */ + ret = nvkm_notify_init(NULL, &i2c->event, nvkm_dp_hpd, true, + &(struct nvkm_i2c_ntfy_req) { + .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG | + NVKM_I2C_IRQ, + .port = outp->dp.aux->id, + }, + sizeof(struct nvkm_i2c_ntfy_req), + sizeof(struct nvkm_i2c_ntfy_rep), + &outp->dp.hpd); + if (ret) { + OUTP_ERR(outp, "error monitoring aux hpd: %d", ret); + return ret; + } + + mutex_init(&outp->dp.mutex); + atomic_set(&outp->dp.lt.done, 0); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h new file mode 100644 index 000000000..1d86baa6a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_DP_H__ +#define __NVKM_DISP_DP_H__ +#include "outp.h" + +int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *, + struct nvkm_outp **); +void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *); + +/* DPCD Receiver Capabilities */ +#define DPCD_RC00_DPCD_REV 0x00000 +#define DPCD_RC01_MAX_LINK_RATE 0x00001 +#define DPCD_RC02 0x00002 +#define DPCD_RC02_ENHANCED_FRAME_CAP 0x80 +#define DPCD_RC02_TPS3_SUPPORTED 0x40 +#define DPCD_RC02_MAX_LANE_COUNT 0x1f +#define DPCD_RC03 0x00003 +#define DPCD_RC03_TPS4_SUPPORTED 0x80 +#define DPCD_RC03_MAX_DOWNSPREAD 0x01 +#define DPCD_RC0E 0x0000e +#define DPCD_RC0E_AUX_RD_INTERVAL 0x7f +#define DPCD_RC10_SUPPORTED_LINK_RATES(i) 0x00010 +#define DPCD_RC10_SUPPORTED_LINK_RATES__SIZE 16 + +/* DPCD Link Configuration */ +#define DPCD_LC00_LINK_BW_SET 0x00100 +#define DPCD_LC01 0x00101 +#define DPCD_LC01_ENHANCED_FRAME_EN 0x80 +#define DPCD_LC01_LANE_COUNT_SET 0x1f +#define DPCD_LC02 0x00102 +#define DPCD_LC02_TRAINING_PATTERN_SET 0x0f +#define DPCD_LC02_SCRAMBLING_DISABLE 0x20 +#define DPCD_LC03(l) ((l) + 0x00103) +#define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED 0x20 +#define DPCD_LC03_PRE_EMPHASIS_SET 0x18 +#define DPCD_LC03_MAX_SWING_REACHED 0x04 +#define DPCD_LC03_VOLTAGE_SWING_SET 0x03 +#define DPCD_LC0F 0x0010f +#define DPCD_LC0F_LANE1_MAX_POST_CURSOR2_REACHED 0x40 +#define DPCD_LC0F_LANE1_POST_CURSOR2_SET 0x30 +#define DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED 0x04 +#define DPCD_LC0F_LANE0_POST_CURSOR2_SET 0x03 +#define DPCD_LC10 0x00110 +#define DPCD_LC10_LANE3_MAX_POST_CURSOR2_REACHED 0x40 +#define DPCD_LC10_LANE3_POST_CURSOR2_SET 0x30 +#define DPCD_LC10_LANE2_MAX_POST_CURSOR2_REACHED 0x04 +#define DPCD_LC10_LANE2_POST_CURSOR2_SET 0x03 +#define DPCD_LC15_LINK_RATE_SET 0x00115 +#define DPCD_LC15_LINK_RATE_SET_MASK 0x07 + +/* DPCD Link/Sink Status */ +#define DPCD_LS02 0x00202 +#define DPCD_LS02_LANE1_SYMBOL_LOCKED 0x40 +#define DPCD_LS02_LANE1_CHANNEL_EQ_DONE 0x20 +#define DPCD_LS02_LANE1_CR_DONE 0x10 +#define DPCD_LS02_LANE0_SYMBOL_LOCKED 0x04 +#define DPCD_LS02_LANE0_CHANNEL_EQ_DONE 0x02 +#define DPCD_LS02_LANE0_CR_DONE 0x01 +#define DPCD_LS03 0x00203 +#define DPCD_LS03_LANE3_SYMBOL_LOCKED 0x40 +#define DPCD_LS03_LANE3_CHANNEL_EQ_DONE 0x20 +#define DPCD_LS03_LANE3_CR_DONE 0x10 +#define DPCD_LS03_LANE2_SYMBOL_LOCKED 0x04 +#define DPCD_LS03_LANE2_CHANNEL_EQ_DONE 0x02 +#define DPCD_LS03_LANE2_CR_DONE 0x01 +#define DPCD_LS04 0x00204 +#define DPCD_LS04_LINK_STATUS_UPDATED 0x80 +#define DPCD_LS04_DOWNSTREAM_PORT_STATUS_CHANGED 0x40 +#define DPCD_LS04_INTERLANE_ALIGN_DONE 0x01 +#define DPCD_LS06 0x00206 +#define DPCD_LS06_LANE1_PRE_EMPHASIS 0xc0 +#define DPCD_LS06_LANE1_VOLTAGE_SWING 0x30 +#define DPCD_LS06_LANE0_PRE_EMPHASIS 0x0c +#define DPCD_LS06_LANE0_VOLTAGE_SWING 0x03 +#define DPCD_LS07 0x00207 +#define DPCD_LS07_LANE3_PRE_EMPHASIS 0xc0 +#define DPCD_LS07_LANE3_VOLTAGE_SWING 0x30 +#define DPCD_LS07_LANE2_PRE_EMPHASIS 0x0c +#define DPCD_LS07_LANE2_VOLTAGE_SWING 0x03 +#define DPCD_LS0C 0x0020c +#define DPCD_LS0C_LANE3_POST_CURSOR2 0xc0 +#define DPCD_LS0C_LANE2_POST_CURSOR2 0x30 +#define DPCD_LS0C_LANE1_POST_CURSOR2 0x0c +#define DPCD_LS0C_LANE0_POST_CURSOR2 0x03 + +/* DPCD Sink Control */ +#define DPCD_SC00 0x00600 +#define DPCD_SC00_SET_POWER 0x03 +#define DPCD_SC00_SET_POWER_D0 0x01 +#define DPCD_SC00_SET_POWER_D3 0x03 + +#define DPCD_LTTPR_REV 0xf0000 +#define DPCD_LTTPR_MODE 0xf0003 +#define DPCD_LTTPR_MODE_TRANSPARENT 0x55 +#define DPCD_LTTPR_MODE_NON_TRANSPARENT 0xaa +#define DPCD_LTTPR_PATTERN_SET(i) ((i - 1) * 0x50 + 0xf0010) +#define DPCD_LTTPR_LANE0_SET(i) ((i - 1) * 0x50 + 0xf0011) +#define DPCD_LTTPR_AUX_RD_INTERVAL(i) ((i - 1) * 0x50 + 0xf0020) +#define DPCD_LTTPR_LANE0_1_STATUS(i) ((i - 1) * 0x50 + 0xf0030) +#define DPCD_LTTPR_LANE0_1_ADJUST(i) ((i - 1) * 0x50 + 0xf0033) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c new file mode 100644 index 000000000..4966a51af --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c @@ -0,0 +1,327 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "hdmi.h" +#include "head.h" +#include "ior.h" + +#include + +void +g84_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + 0x1f000000 /* ??? */ | + max_ac_packet << 16 | + rekey; + const u32 hoff = head * 0x800; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; + + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); + + if (!(ctrl & 0x40000000)) { + nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61653c + hoff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); + return; + } + + /* AVI InfoFrame */ + nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); + if (avi_size) { + nvkm_wr32(device, 0x616528 + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x61652c + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616530 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616534 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x616538 + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); + } + + /* Audio InfoFrame */ + nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); + nvkm_wr32(device, 0x616508 + hoff, 0x000a0184); + nvkm_wr32(device, 0x61650c + hoff, 0x00000071); + nvkm_wr32(device, 0x616510 + hoff, 0x00000000); + nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001); + + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000); + if (vendor_size) { + nvkm_wr32(device, 0x616544 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x616548 + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61654c + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x616550 + hoff, vendor_infoframe->subpack1_low); */ + /* nvkm_wr32(device, 0x616554 + hoff, vendor_infoframe->subpack1_high); */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001); + } + + nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ + nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ + nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ + + /* ??? */ + nvkm_mask(device, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nvkm_mask(device, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nvkm_mask(device, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* HDMI_CTRL */ + nvkm_mask(device, 0x6165a4 + hoff, 0x5f1f007f, ctrl); +} + +static const struct nvkm_ior_func +g84_sor = { + .state = nv50_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = g84_sor_hdmi_ctrl, + }, +}; + +int +g84_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&g84_sor, disp, SOR, id, false); +} + +static const struct nvkm_disp_mthd_list +g84_disp_ovly_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x000000 }, + { 0x0084, 0x6109a0 }, + { 0x0088, 0x6109c0 }, + { 0x008c, 0x6109c8 }, + { 0x0090, 0x6109b4 }, + { 0x0094, 0x610970 }, + { 0x00a0, 0x610998 }, + { 0x00a4, 0x610964 }, + { 0x00c0, 0x610958 }, + { 0x00e0, 0x6109a8 }, + { 0x00e4, 0x6109d0 }, + { 0x00e8, 0x6109d8 }, + { 0x0100, 0x61094c }, + { 0x0104, 0x610984 }, + { 0x0108, 0x61098c }, + { 0x0800, 0x6109f8 }, + { 0x0808, 0x610a08 }, + { 0x080c, 0x610a10 }, + { 0x0810, 0x610a00 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +g84_disp_ovly_mthd = { + .name = "Overlay", + .addr = 0x000540, + .prev = 0x000004, + .data = { + { "Global", 1, &g84_disp_ovly_mthd_base }, + {} + } +}; + +const struct nvkm_disp_chan_user +g84_disp_ovly = { + .func = &nv50_disp_dmac_func, + .ctrl = 3, + .user = 3, + .mthd = &g84_disp_ovly_mthd, +}; + +static const struct nvkm_disp_mthd_list +g84_disp_base_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x000000 }, + { 0x0084, 0x0008c4 }, + { 0x0088, 0x0008d0 }, + { 0x008c, 0x0008dc }, + { 0x0090, 0x0008e4 }, + { 0x0094, 0x610884 }, + { 0x00a0, 0x6108a0 }, + { 0x00a4, 0x610878 }, + { 0x00c0, 0x61086c }, + { 0x00c4, 0x610800 }, + { 0x00c8, 0x61080c }, + { 0x00cc, 0x610818 }, + { 0x00e0, 0x610858 }, + { 0x00e4, 0x610860 }, + { 0x00e8, 0x6108ac }, + { 0x00ec, 0x6108b4 }, + { 0x00fc, 0x610824 }, + { 0x0100, 0x610894 }, + { 0x0104, 0x61082c }, + { 0x0110, 0x6108bc }, + { 0x0114, 0x61088c }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +g84_disp_base_mthd = { + .name = "Base", + .addr = 0x000540, + .prev = 0x000004, + .data = { + { "Global", 1, &g84_disp_base_mthd_base }, + { "Image", 2, &nv50_disp_base_mthd_image }, + {} + } +}; + +const struct nvkm_disp_chan_user +g84_disp_base = { + .func = &nv50_disp_dmac_func, + .ctrl = 1, + .user = 1, + .mthd = &g84_disp_base_mthd, +}; + +const struct nvkm_disp_mthd_list +g84_disp_core_mthd_dac = { + .mthd = 0x0080, + .addr = 0x000008, + .data = { + { 0x0400, 0x610b58 }, + { 0x0404, 0x610bdc }, + { 0x0420, 0x610bc4 }, + {} + } +}; + +const struct nvkm_disp_mthd_list +g84_disp_core_mthd_head = { + .mthd = 0x0400, + .addr = 0x000540, + .data = { + { 0x0800, 0x610ad8 }, + { 0x0804, 0x610ad0 }, + { 0x0808, 0x610a48 }, + { 0x080c, 0x610a78 }, + { 0x0810, 0x610ac0 }, + { 0x0814, 0x610af8 }, + { 0x0818, 0x610b00 }, + { 0x081c, 0x610ae8 }, + { 0x0820, 0x610af0 }, + { 0x0824, 0x610b08 }, + { 0x0828, 0x610b10 }, + { 0x082c, 0x610a68 }, + { 0x0830, 0x610a60 }, + { 0x0834, 0x000000 }, + { 0x0838, 0x610a40 }, + { 0x0840, 0x610a24 }, + { 0x0844, 0x610a2c }, + { 0x0848, 0x610aa8 }, + { 0x084c, 0x610ab0 }, + { 0x085c, 0x610c5c }, + { 0x0860, 0x610a84 }, + { 0x0864, 0x610a90 }, + { 0x0868, 0x610b18 }, + { 0x086c, 0x610b20 }, + { 0x0870, 0x610ac8 }, + { 0x0874, 0x610a38 }, + { 0x0878, 0x610c50 }, + { 0x0880, 0x610a58 }, + { 0x0884, 0x610a9c }, + { 0x089c, 0x610c68 }, + { 0x08a0, 0x610a70 }, + { 0x08a4, 0x610a50 }, + { 0x08a8, 0x610ae0 }, + { 0x08c0, 0x610b28 }, + { 0x08c4, 0x610b30 }, + { 0x08c8, 0x610b40 }, + { 0x08d4, 0x610b38 }, + { 0x08d8, 0x610b48 }, + { 0x08dc, 0x610b50 }, + { 0x0900, 0x610a18 }, + { 0x0904, 0x610ab8 }, + { 0x0910, 0x610c70 }, + { 0x0914, 0x610c78 }, + {} + } +}; + +const struct nvkm_disp_chan_mthd +g84_disp_core_mthd = { + .name = "Core", + .addr = 0x000000, + .prev = 0x000004, + .data = { + { "Global", 1, &nv50_disp_core_mthd_base }, + { "DAC", 3, &g84_disp_core_mthd_dac }, + { "SOR", 2, &nv50_disp_core_mthd_sor }, + { "PIOR", 3, &nv50_disp_core_mthd_pior }, + { "HEAD", 2, &g84_disp_core_mthd_head }, + {} + } +}; + +const struct nvkm_disp_chan_user +g84_disp_core = { + .func = &nv50_disp_core_func, + .ctrl = 0, + .user = 0, + .mthd = &g84_disp_core_mthd, +}; + +static const struct nvkm_disp_func +g84_disp = { + .oneinit = nv50_disp_oneinit, + .init = nv50_disp_init, + .fini = nv50_disp_fini, + .intr = nv50_disp_intr, + .super = nv50_disp_super, + .uevent = &nv50_disp_chan_uevent, + .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, + .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, + .sor = { .cnt = nv50_sor_cnt, .new = g84_sor_new }, + .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, + .root = { 0,0,G82_DISP }, + .user = { + {{0,0,G82_DISP_CURSOR }, nvkm_disp_chan_new, &nv50_disp_curs }, + {{0,0,G82_DISP_OVERLAY }, nvkm_disp_chan_new, &nv50_disp_oimm }, + {{0,0,G82_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, & g84_disp_base }, + {{0,0,G82_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, & g84_disp_core }, + {{0,0,G82_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, & g84_disp_ovly }, + {} + }, +}; + +int +g84_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&g84_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c new file mode 100644 index 000000000..a4853c4e5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c @@ -0,0 +1,377 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +#include + +void +g94_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + + nvkm_mask(device, 0x61c128 + loff, 0x0000003f, watermark); +} + +void +g94_sor_dp_activesym(struct nvkm_ior *sor, int head, + u8 TU, u8 VTUa, u8 VTUf, u8 VTUi) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + + nvkm_mask(device, 0x61c10c + loff, 0x000001fc, TU << 2); + nvkm_mask(device, 0x61c128 + loff, 0x010f7f00, VTUa << 24 | VTUf << 16 | VTUi << 8); +} + +void +g94_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + + nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, h); + nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, v); +} + +void +g94_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + const u32 shift = sor->func->dp->lanes[ln] * 8; + u32 data[3]; + + data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nvkm_rd32(device, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (pu << 8); + + nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); + nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); + nvkm_wr32(device, 0x61c130 + loff, data[2]); +} + +void +g94_sor_dp_pattern(struct nvkm_ior *sor, int pattern) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + u32 data; + + switch (pattern) { + case 0: data = 0x00001000; break; + case 1: data = 0x01000000; break; + case 2: data = 0x02000000; break; + default: + WARN_ON(1); + return; + } + + nvkm_mask(device, 0x61c10c + loff, 0x0f001000, data); +} + +void +g94_sor_dp_power(struct nvkm_ior *sor, int nr) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); + u32 mask = 0, i; + + for (i = 0; i < nr; i++) + mask |= 1 << sor->func->dp->lanes[i]; + + nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask); + nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000)) + break; + ); +} + +int +g94_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + + dpctrl |= ((1 << sor->dp.nr) - 1) << 16; + if (sor->dp.ef) + dpctrl |= 0x00004000; + if (sor->dp.bw > 0x06) + clksor |= 0x00040000; + + nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor); + nvkm_mask(device, 0x61c10c + loff, 0x001f4000, dpctrl); + return 0; +} + +const struct nvkm_ior_func_dp +g94_sor_dp = { + .lanes = { 2, 1, 0, 3}, + .links = g94_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = g94_sor_dp_pattern, + .drive = g94_sor_dp_drive, + .audio_sym = g94_sor_dp_audio_sym, + .activesym = g94_sor_dp_activesym, + .watermark = g94_sor_dp_watermark, +}; + +static bool +g94_sor_war_needed(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + + if (sor->asy.proto == TMDS) { + switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) { + case 0x00000000: + case 0x00030000: + return true; + default: + break; + } + } + + return false; +} + +static void +g94_sor_war_update_sppll1(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_ior *ior; + bool used = false; + u32 clksor; + + list_for_each_entry(ior, &disp->iors, head) { + if (ior->type != SOR) + continue; + + clksor = nvkm_rd32(device, 0x614300 + nv50_ior_base(ior)); + switch (clksor & 0x03000000) { + case 0x02000000: + case 0x03000000: + used = true; + break; + default: + break; + } + } + + if (used) + return; + + nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000); +} + +static void +g94_sor_war_3(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + u32 sorpwr; + + if (!g94_sor_war_needed(sor)) + return; + + sorpwr = nvkm_rd32(device, 0x61c004 + soff); + if (sorpwr & 0x00000001) { + u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); + u32 pd_pc = (seqctl & 0x00000f00) >> 8; + u32 pu_pc = seqctl & 0x0000000f; + + nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000); + + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) + break; + ); + nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) + break; + ); + + nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000); + nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000); + } + + nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000); + + if (sorpwr & 0x00000001) + nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001); + + g94_sor_war_update_sppll1(sor->disp); +} + +static void +g94_sor_war_2(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + + if (!g94_sor_war_needed(sor)) + return; + + nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000); + nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000); + nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001); + + nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000); + nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000); + nvkm_usec(device, 400, NVKM_DELAY); + nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000); + nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000); + + if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) { + u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); + u32 pu_pc = seqctl & 0x0000000f; + nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000); + } +} + +void +g94_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 coff = sor->id * 8 + (state == &sor->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610794 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = LVDS; state->link = 1; break; + case 1: state->proto = TMDS; state->link = 1; break; + case 2: state->proto = TMDS; state->link = 2; break; + case 5: state->proto = TMDS; state->link = 3; break; + case 8: state->proto = DP; state->link = 1; break; + case 9: state->proto = DP; state->link = 2; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x00000003; + nv50_pior_depth(sor, state, ctrl); +} + +static const struct nvkm_ior_func +g94_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .war_2 = g94_sor_war_2, + .war_3 = g94_sor_war_3, + .dp = &g94_sor_dp, +}; + +static int +g94_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&g94_sor, disp, SOR, id, false); +} + +int +g94_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + *pmask = (nvkm_rd32(device, 0x610184) & 0x0f000000) >> 24; + return 4; +} + +static const struct nvkm_disp_mthd_list +g94_disp_core_mthd_sor = { + .mthd = 0x0040, + .addr = 0x000008, + .data = { + { 0x0600, 0x610794 }, + {} + } +}; + +const struct nvkm_disp_chan_mthd +g94_disp_core_mthd = { + .name = "Core", + .addr = 0x000000, + .prev = 0x000004, + .data = { + { "Global", 1, &nv50_disp_core_mthd_base }, + { "DAC", 3, &g84_disp_core_mthd_dac }, + { "SOR", 4, &g94_disp_core_mthd_sor }, + { "PIOR", 3, &nv50_disp_core_mthd_pior }, + { "HEAD", 2, &g84_disp_core_mthd_head }, + {} + } +}; + +const struct nvkm_disp_chan_user +g94_disp_core = { + .func = &nv50_disp_core_func, + .ctrl = 0, + .user = 0, + .mthd = &g94_disp_core_mthd, +}; + +static const struct nvkm_disp_func +g94_disp = { + .oneinit = nv50_disp_oneinit, + .init = nv50_disp_init, + .fini = nv50_disp_fini, + .intr = nv50_disp_intr, + .super = nv50_disp_super, + .uevent = &nv50_disp_chan_uevent, + .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, + .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, + .sor = { .cnt = g94_sor_cnt, .new = g94_sor_new }, + .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, + .root = { 0,0,GT206_DISP }, + .user = { + {{0,0, G82_DISP_CURSOR }, nvkm_disp_chan_new, & nv50_disp_curs }, + {{0,0, G82_DISP_OVERLAY }, nvkm_disp_chan_new, & nv50_disp_oimm }, + {{0,0,GT200_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, & g84_disp_base }, + {{0,0,GT206_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, & g94_disp_core }, + {{0,0,GT200_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, >200_disp_ovly }, + {} + }, +}; + +int +g94_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&g94_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c new file mode 100644 index 000000000..7489d0d7f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c @@ -0,0 +1,153 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +#include + +static int +ga102_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + + switch (sor->dp.bw) { + case 0x06: clksor |= 0x00000000; break; + case 0x0a: clksor |= 0x00040000; break; + case 0x14: clksor |= 0x00080000; break; + case 0x1e: clksor |= 0x000c0000; break; + case 0x08: clksor |= 0x00100000; break; + case 0x09: clksor |= 0x00140000; break; + case 0x0c: clksor |= 0x00180000; break; + case 0x10: clksor |= 0x001c0000; break; + default: + WARN_ON(1); + return -EINVAL; + } + + dpctrl |= ((1 << sor->dp.nr) - 1) << 16; + if (sor->dp.mst) + dpctrl |= 0x40000000; + if (sor->dp.ef) + dpctrl |= 0x00004000; + + nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor); + + /*XXX*/ + nvkm_msec(device, 40, NVKM_DELAY); + nvkm_mask(device, 0x612300 + soff, 0x00030000, 0x00010000); + nvkm_mask(device, 0x61c10c + loff, 0x00000003, 0x00000001); + + nvkm_mask(device, 0x61c10c + loff, 0x401f4000, dpctrl); + return 0; +} + +static const struct nvkm_ior_func_dp +ga102_sor_dp = { + .lanes = { 0, 1, 2, 3 }, + .links = ga102_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gm107_sor_dp_pattern, + .drive = gm200_sor_dp_drive, + .vcpi = tu102_sor_dp_vcpi, + .audio = gv100_sor_dp_audio, + .audio_sym = gv100_sor_dp_audio_sym, + .watermark = gv100_sor_dp_watermark, +}; + +static void +ga102_sor_clock(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + u32 div2 = 0; + + if (sor->asy.proto == TMDS) { + if (sor->tmds.high_speed) + div2 = 1; + } + + nvkm_wr32(device, 0x00ec08 + (sor->id * 0x10), 0x00000000); + nvkm_wr32(device, 0x00ec04 + (sor->id * 0x10), div2); +} + +static const struct nvkm_ior_func +ga102_sor = { + .route = { + .get = gm200_sor_route_get, + .set = gm200_sor_route_set, + }, + .state = gv100_sor_state, + .power = nv50_sor_power, + .clock = ga102_sor_clock, + .hdmi = { + .ctrl = gv100_sor_hdmi_ctrl, + .scdc = gm200_sor_hdmi_scdc, + }, + .dp = &ga102_sor_dp, + .hda = &gv100_sor_hda, +}; + +static int +ga102_sor_new(struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + u32 hda = nvkm_rd32(device, 0x08a15c); + + return nvkm_ior_new_(&ga102_sor, disp, SOR, id, hda & BIT(id)); +} + +static const struct nvkm_disp_func +ga102_disp = { + .oneinit = nv50_disp_oneinit, + .init = tu102_disp_init, + .fini = gv100_disp_fini, + .intr = gv100_disp_intr, + .super = gv100_disp_super, + .uevent = &gv100_disp_chan_uevent, + .wndw = { .cnt = gv100_disp_wndw_cnt }, + .head = { .cnt = gv100_head_cnt, .new = gv100_head_new }, + .sor = { .cnt = gv100_sor_cnt, .new = ga102_sor_new }, + .ramht_size = 0x2000, + .root = { 0, 0,GA102_DISP }, + .user = { + {{-1,-1,GV100_DISP_CAPS }, gv100_disp_caps_new }, + {{ 0, 0,GA102_DISP_CURSOR }, nvkm_disp_chan_new, &gv100_disp_curs }, + {{ 0, 0,GA102_DISP_WINDOW_IMM_CHANNEL_DMA}, nvkm_disp_wndw_new, &gv100_disp_wimm }, + {{ 0, 0,GA102_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gv100_disp_core }, + {{ 0, 0,GA102_DISP_WINDOW_CHANNEL_DMA }, nvkm_disp_wndw_new, &gv100_disp_wndw }, + {} + }, +}; + +int +ga102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&ga102_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c new file mode 100644 index 000000000..39822f1b5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c @@ -0,0 +1,1240 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "hdmi.h" +#include "head.h" +#include "ior.h" +#include "outp.h" + +#include +#include + +#include + +static void +gf119_sor_hda_device_entry(struct nvkm_ior *ior, int head) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 hoff = 0x800 * head; + + nvkm_mask(device, 0x616548 + hoff, 0x00000070, head << 4); +} + +void +gf119_sor_hda_eld(struct nvkm_ior *ior, int head, u8 *data, u8 size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = 0x030 * ior->id + (head * 0x04); + int i; + + for (i = 0; i < size; i++) + nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | data[i]); + for (; i < 0x60; i++) + nvkm_wr32(device, 0x10ec00 + soff, (i << 8)); + nvkm_mask(device, 0x10ec10 + soff, 0x80000002, 0x80000002); +} + +void +gf119_sor_hda_hpd(struct nvkm_ior *ior, int head, bool present) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = 0x030 * ior->id + (head * 0x04); + u32 data = 0x80000000; + u32 mask = 0x80000001; + + if (present) { + ior->func->hda->device_entry(ior, head); + data |= 0x00000001; + } else { + mask |= 0x00000002; + } + + nvkm_mask(device, 0x10ec10 + soff, mask, data); +} + +const struct nvkm_ior_func_hda +gf119_sor_hda = { + .hpd = gf119_sor_hda_hpd, + .eld = gf119_sor_hda_eld, + .device_entry = gf119_sor_hda_device_entry, +}; + +void +gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + + nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark); +} + +void +gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + + nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h); + nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v); +} + +void +gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = 0x800 * head; + const u32 data = 0x80000000 | (0x00000001 * enable); + const u32 mask = 0x8000000d; + + nvkm_mask(device, 0x616618 + hoff, mask, data); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x616618 + hoff) & 0x80000000)) + break; + ); +} + +void +gf119_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + + nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot); + nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn); +} + +void +gf119_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + const u32 shift = sor->func->dp->lanes[ln] * 8; + u32 data[4]; + + data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nvkm_rd32(device, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (pu << 8); + + nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); + nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); + nvkm_wr32(device, 0x61c130 + loff, data[2]); + + data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); + nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); +} + +static void +gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + u32 data; + + switch (pattern) { + case 0: data = 0x10101010; break; + case 1: data = 0x01010101; break; + case 2: data = 0x02020202; break; + case 3: data = 0x03030303; break; + default: + WARN_ON(1); + return; + } + + nvkm_mask(device, 0x61c110 + soff, 0x1f1f1f1f, data); +} + +int +gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + + clksor |= sor->dp.bw << 18; + dpctrl |= ((1 << sor->dp.nr) - 1) << 16; + if (sor->dp.mst) + dpctrl |= 0x40000000; + if (sor->dp.ef) + dpctrl |= 0x00004000; + + nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor); + nvkm_mask(device, 0x61c10c + loff, 0x401f4000, dpctrl); + return 0; +} + +const struct nvkm_ior_func_dp +gf119_sor_dp = { + .lanes = { 2, 1, 0, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gf119_sor_dp_pattern, + .drive = gf119_sor_dp_drive, + .vcpi = gf119_sor_dp_vcpi, + .audio = gf119_sor_dp_audio, + .audio_sym = gf119_sor_dp_audio_sym, + .watermark = gf119_sor_dp_watermark, +}; + +static void +gf119_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + max_ac_packet << 16 | + rekey; + const u32 hoff = head * 0x800; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; + + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); + + if (!(ctrl & 0x40000000)) { + nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); + return; + } + + /* AVI InfoFrame */ + nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); + if (avi_size) { + nvkm_wr32(device, 0x61671c + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x616720 + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616724 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616728 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61672c + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000); + if (vendor_size) { + /* + * These appear to be the audio infoframe registers, + * but no other set of infoframe registers has yet + * been found. + */ + nvkm_wr32(device, 0x616738 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x61673c + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x616740 + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001); + } + + /* ??? InfoFrame? */ + nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); + nvkm_wr32(device, 0x6167ac + hoff, 0x00000010); + nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000001); + + /* HDMI_CTRL */ + nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); +} + +void +gf119_sor_clock(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + u32 div1 = sor->asy.link == 3; + u32 div2 = sor->asy.link == 3; + + if (sor->asy.proto == TMDS) { + const u32 speed = sor->tmds.high_speed ? 0x14 : 0x0a; + nvkm_mask(device, 0x612300 + soff, 0x007c0000, speed << 18); + if (sor->tmds.high_speed) + div2 = 1; + } + + nvkm_mask(device, 0x612300 + soff, 0x00000707, (div2 << 8) | div1); +} + +void +gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 coff = (state == &sor->asy) * 0x20000 + sor->id * 0x20; + u32 ctrl = nvkm_rd32(device, 0x640200 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = LVDS; state->link = 1; break; + case 1: state->proto = TMDS; state->link = 1; break; + case 2: state->proto = TMDS; state->link = 2; break; + case 5: state->proto = TMDS; state->link = 3; break; + case 8: state->proto = DP; state->link = 1; break; + case 9: state->proto = DP; state->link = 2; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x0000000f; +} + +static const struct nvkm_ior_func +gf119_sor = { + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gf119_sor_hdmi_ctrl, + }, + .dp = &gf119_sor_dp, + .hda = &gf119_sor_hda, +}; + +static int +gf119_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&gf119_sor, disp, SOR, id, true); +} + +int +gf119_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + *pmask = (nvkm_rd32(device, 0x612004) & 0x0000ff00) >> 8; + return 8; +} + +static void +gf119_dac_clock(struct nvkm_ior *dac) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); + nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000); +} + +static void +gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 coff = (state == &dac->asy) * 0x20000 + dac->id * 0x20; + u32 ctrl = nvkm_rd32(device, 0x640180 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = CRT; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x0000000f; +} + +static const struct nvkm_ior_func +gf119_dac = { + .state = gf119_dac_state, + .power = nv50_dac_power, + .sense = nv50_dac_sense, + .clock = gf119_dac_clock, +}; + +int +gf119_dac_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&gf119_dac, disp, DAC, id, false); +} + +int +gf119_dac_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + *pmask = (nvkm_rd32(device, 0x612004) & 0x000000f0) >> 4; + return 4; +} + +static void +gf119_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x800; + nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000000); +} + +static void +gf119_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x800; + nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001); +} + +void +gf119_head_rgclk(struct nvkm_head *head, int div) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div); +} + +static void +gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = (state == &head->asy) * 0x20000 + head->id * 0x300; + u32 data; + + data = nvkm_rd32(device, 0x640414 + hoff); + state->vtotal = (data & 0xffff0000) >> 16; + state->htotal = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x640418 + hoff); + state->vsynce = (data & 0xffff0000) >> 16; + state->hsynce = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x64041c + hoff); + state->vblanke = (data & 0xffff0000) >> 16; + state->hblanke = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x640420 + hoff); + state->vblanks = (data & 0xffff0000) >> 16; + state->hblanks = (data & 0x0000ffff); + state->hz = nvkm_rd32(device, 0x640450 + hoff); + + data = nvkm_rd32(device, 0x640404 + hoff); + switch ((data & 0x000003c0) >> 6) { + case 6: state->or.depth = 30; break; + case 5: state->or.depth = 24; break; + case 2: state->or.depth = 18; break; + case 0: state->or.depth = 18; break; /*XXX: "default" */ + default: + state->or.depth = 18; + WARN_ON(1); + break; + } +} + +static const struct nvkm_head_func +gf119_head = { + .state = gf119_head_state, + .rgpos = nv50_head_rgpos, + .rgclk = gf119_head_rgclk, + .vblank_get = gf119_head_vblank_get, + .vblank_put = gf119_head_vblank_put, +}; + +int +gf119_head_new(struct nvkm_disp *disp, int id) +{ + return nvkm_head_new_(&gf119_head, disp, id); +} + +int +gf119_head_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + *pmask = nvkm_rd32(device, 0x612004) & 0x0000000f; + return nvkm_rd32(device, 0x022448); +} + +static void +gf119_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent); + struct nvkm_device *device = disp->engine.subdev.device; + nvkm_mask(device, 0x610090, 0x00000001 << index, 0x00000000 << index); + nvkm_wr32(device, 0x61008c, 0x00000001 << index); +} + +static void +gf119_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent); + struct nvkm_device *device = disp->engine.subdev.device; + nvkm_wr32(device, 0x61008c, 0x00000001 << index); + nvkm_mask(device, 0x610090, 0x00000001 << index, 0x00000001 << index); +} + +const struct nvkm_event_func +gf119_disp_chan_uevent = { + .init = gf119_disp_chan_uevent_init, + .fini = gf119_disp_chan_uevent_fini, +}; + +void +gf119_disp_chan_intr(struct nvkm_disp_chan *chan, bool en) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 mask = 0x00000001 << chan->chid.user; + if (!en) { + nvkm_mask(device, 0x610090, mask, 0x00000000); + nvkm_mask(device, 0x6100a0, mask, 0x00000000); + } else { + nvkm_mask(device, 0x6100a0, mask, mask); + } +} + +static void +gf119_disp_pioc_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_disp *disp = chan->disp; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + nvkm_mask(device, 0x610490 + (ctrl * 0x10), 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610490 + (ctrl * 0x10)) & 0x00030000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d fini: %08x\n", user, + nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); + } +} + +static int +gf119_disp_pioc_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_disp *disp = chan->disp; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + /* activate channel */ + nvkm_wr32(device, 0x610490 + (ctrl * 0x10), 0x00000001); + if (nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x610490 + (ctrl * 0x10)); + if ((tmp & 0x00030000) == 0x00010000) + break; + ) < 0) { + nvkm_error(subdev, "ch %d init: %08x\n", user, + nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); + return -EBUSY; + } + + return 0; +} + +const struct nvkm_disp_chan_func +gf119_disp_pioc_func = { + .init = gf119_disp_pioc_init, + .fini = gf119_disp_pioc_fini, + .intr = gf119_disp_chan_intr, + .user = nv50_disp_chan_user, +}; + +int +gf119_disp_dmac_bind(struct nvkm_disp_chan *chan, struct nvkm_object *object, u32 handle) +{ + return nvkm_ramht_insert(chan->disp->ramht, object, chan->chid.user, -9, handle, + chan->chid.user << 27 | 0x00000001); +} + +void +gf119_disp_dmac_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + /* deactivate channel */ + nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00001010, 0x00001000); + nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000003, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610490 + (ctrl * 0x10)) & 0x001e0000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d fini: %08x\n", user, + nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); + } + + chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000)); +} + +static int +gf119_disp_dmac_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + /* initialise channel for dma command submission */ + nvkm_wr32(device, 0x610494 + (ctrl * 0x0010), chan->push); + nvkm_wr32(device, 0x610498 + (ctrl * 0x0010), 0x00010000); + nvkm_wr32(device, 0x61049c + (ctrl * 0x0010), 0x00000001); + nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000010, 0x00000010); + nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put); + nvkm_wr32(device, 0x610490 + (ctrl * 0x0010), 0x00000013); + + /* wait for it to go inactive */ + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610490 + (ctrl * 0x10)) & 0x80000000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d init: %08x\n", user, + nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); + return -EBUSY; + } + + return 0; +} + +const struct nvkm_disp_chan_func +gf119_disp_dmac_func = { + .push = nv50_disp_dmac_push, + .init = gf119_disp_dmac_init, + .fini = gf119_disp_dmac_fini, + .intr = gf119_disp_chan_intr, + .user = nv50_disp_chan_user, + .bind = gf119_disp_dmac_bind, +}; + +const struct nvkm_disp_chan_user +gf119_disp_curs = { + .func = &gf119_disp_pioc_func, + .ctrl = 13, + .user = 13, +}; + +const struct nvkm_disp_chan_user +gf119_disp_oimm = { + .func = &gf119_disp_pioc_func, + .ctrl = 9, + .user = 9, +}; + +static const struct nvkm_disp_mthd_list +gf119_disp_ovly_mthd_base = { + .mthd = 0x0000, + .data = { + { 0x0080, 0x665080 }, + { 0x0084, 0x665084 }, + { 0x0088, 0x665088 }, + { 0x008c, 0x66508c }, + { 0x0090, 0x665090 }, + { 0x0094, 0x665094 }, + { 0x00a0, 0x6650a0 }, + { 0x00a4, 0x6650a4 }, + { 0x00b0, 0x6650b0 }, + { 0x00b4, 0x6650b4 }, + { 0x00b8, 0x6650b8 }, + { 0x00c0, 0x6650c0 }, + { 0x00e0, 0x6650e0 }, + { 0x00e4, 0x6650e4 }, + { 0x00e8, 0x6650e8 }, + { 0x0100, 0x665100 }, + { 0x0104, 0x665104 }, + { 0x0108, 0x665108 }, + { 0x010c, 0x66510c }, + { 0x0110, 0x665110 }, + { 0x0118, 0x665118 }, + { 0x011c, 0x66511c }, + { 0x0120, 0x665120 }, + { 0x0124, 0x665124 }, + { 0x0130, 0x665130 }, + { 0x0134, 0x665134 }, + { 0x0138, 0x665138 }, + { 0x013c, 0x66513c }, + { 0x0140, 0x665140 }, + { 0x0144, 0x665144 }, + { 0x0148, 0x665148 }, + { 0x014c, 0x66514c }, + { 0x0150, 0x665150 }, + { 0x0154, 0x665154 }, + { 0x0158, 0x665158 }, + { 0x015c, 0x66515c }, + { 0x0160, 0x665160 }, + { 0x0164, 0x665164 }, + { 0x0168, 0x665168 }, + { 0x016c, 0x66516c }, + { 0x0400, 0x665400 }, + { 0x0408, 0x665408 }, + { 0x040c, 0x66540c }, + { 0x0410, 0x665410 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +gf119_disp_ovly_mthd = { + .name = "Overlay", + .addr = 0x001000, + .prev = -0x020000, + .data = { + { "Global", 1, &gf119_disp_ovly_mthd_base }, + {} + } +}; + +static const struct nvkm_disp_chan_user +gf119_disp_ovly = { + .func = &gf119_disp_dmac_func, + .ctrl = 5, + .user = 5, + .mthd = &gf119_disp_ovly_mthd, +}; + +static const struct nvkm_disp_mthd_list +gf119_disp_base_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x661080 }, + { 0x0084, 0x661084 }, + { 0x0088, 0x661088 }, + { 0x008c, 0x66108c }, + { 0x0090, 0x661090 }, + { 0x0094, 0x661094 }, + { 0x00a0, 0x6610a0 }, + { 0x00a4, 0x6610a4 }, + { 0x00c0, 0x6610c0 }, + { 0x00c4, 0x6610c4 }, + { 0x00c8, 0x6610c8 }, + { 0x00cc, 0x6610cc }, + { 0x00e0, 0x6610e0 }, + { 0x00e4, 0x6610e4 }, + { 0x00e8, 0x6610e8 }, + { 0x00ec, 0x6610ec }, + { 0x00fc, 0x6610fc }, + { 0x0100, 0x661100 }, + { 0x0104, 0x661104 }, + { 0x0108, 0x661108 }, + { 0x010c, 0x66110c }, + { 0x0110, 0x661110 }, + { 0x0114, 0x661114 }, + { 0x0118, 0x661118 }, + { 0x011c, 0x66111c }, + { 0x0130, 0x661130 }, + { 0x0134, 0x661134 }, + { 0x0138, 0x661138 }, + { 0x013c, 0x66113c }, + { 0x0140, 0x661140 }, + { 0x0144, 0x661144 }, + { 0x0148, 0x661148 }, + { 0x014c, 0x66114c }, + { 0x0150, 0x661150 }, + { 0x0154, 0x661154 }, + { 0x0158, 0x661158 }, + { 0x015c, 0x66115c }, + { 0x0160, 0x661160 }, + { 0x0164, 0x661164 }, + { 0x0168, 0x661168 }, + { 0x016c, 0x66116c }, + {} + } +}; + +static const struct nvkm_disp_mthd_list +gf119_disp_base_mthd_image = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0400, 0x661400 }, + { 0x0404, 0x661404 }, + { 0x0408, 0x661408 }, + { 0x040c, 0x66140c }, + { 0x0410, 0x661410 }, + {} + } +}; + +const struct nvkm_disp_chan_mthd +gf119_disp_base_mthd = { + .name = "Base", + .addr = 0x001000, + .prev = -0x020000, + .data = { + { "Global", 1, &gf119_disp_base_mthd_base }, + { "Image", 2, &gf119_disp_base_mthd_image }, + {} + } +}; + +const struct nvkm_disp_chan_user +gf119_disp_base = { + .func = &gf119_disp_dmac_func, + .ctrl = 1, + .user = 1, + .mthd = &gf119_disp_base_mthd, +}; + +const struct nvkm_disp_mthd_list +gf119_disp_core_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x660080 }, + { 0x0084, 0x660084 }, + { 0x0088, 0x660088 }, + { 0x008c, 0x000000 }, + {} + } +}; + +const struct nvkm_disp_mthd_list +gf119_disp_core_mthd_dac = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0180, 0x660180 }, + { 0x0184, 0x660184 }, + { 0x0188, 0x660188 }, + { 0x0190, 0x660190 }, + {} + } +}; + +const struct nvkm_disp_mthd_list +gf119_disp_core_mthd_sor = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0200, 0x660200 }, + { 0x0204, 0x660204 }, + { 0x0208, 0x660208 }, + { 0x0210, 0x660210 }, + {} + } +}; + +const struct nvkm_disp_mthd_list +gf119_disp_core_mthd_pior = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0300, 0x660300 }, + { 0x0304, 0x660304 }, + { 0x0308, 0x660308 }, + { 0x0310, 0x660310 }, + {} + } +}; + +static const struct nvkm_disp_mthd_list +gf119_disp_core_mthd_head = { + .mthd = 0x0300, + .addr = 0x000300, + .data = { + { 0x0400, 0x660400 }, + { 0x0404, 0x660404 }, + { 0x0408, 0x660408 }, + { 0x040c, 0x66040c }, + { 0x0410, 0x660410 }, + { 0x0414, 0x660414 }, + { 0x0418, 0x660418 }, + { 0x041c, 0x66041c }, + { 0x0420, 0x660420 }, + { 0x0424, 0x660424 }, + { 0x0428, 0x660428 }, + { 0x042c, 0x66042c }, + { 0x0430, 0x660430 }, + { 0x0434, 0x660434 }, + { 0x0438, 0x660438 }, + { 0x0440, 0x660440 }, + { 0x0444, 0x660444 }, + { 0x0448, 0x660448 }, + { 0x044c, 0x66044c }, + { 0x0450, 0x660450 }, + { 0x0454, 0x660454 }, + { 0x0458, 0x660458 }, + { 0x045c, 0x66045c }, + { 0x0460, 0x660460 }, + { 0x0468, 0x660468 }, + { 0x046c, 0x66046c }, + { 0x0470, 0x660470 }, + { 0x0474, 0x660474 }, + { 0x0480, 0x660480 }, + { 0x0484, 0x660484 }, + { 0x048c, 0x66048c }, + { 0x0490, 0x660490 }, + { 0x0494, 0x660494 }, + { 0x0498, 0x660498 }, + { 0x04b0, 0x6604b0 }, + { 0x04b8, 0x6604b8 }, + { 0x04bc, 0x6604bc }, + { 0x04c0, 0x6604c0 }, + { 0x04c4, 0x6604c4 }, + { 0x04c8, 0x6604c8 }, + { 0x04d0, 0x6604d0 }, + { 0x04d4, 0x6604d4 }, + { 0x04e0, 0x6604e0 }, + { 0x04e4, 0x6604e4 }, + { 0x04e8, 0x6604e8 }, + { 0x04ec, 0x6604ec }, + { 0x04f0, 0x6604f0 }, + { 0x04f4, 0x6604f4 }, + { 0x04f8, 0x6604f8 }, + { 0x04fc, 0x6604fc }, + { 0x0500, 0x660500 }, + { 0x0504, 0x660504 }, + { 0x0508, 0x660508 }, + { 0x050c, 0x66050c }, + { 0x0510, 0x660510 }, + { 0x0514, 0x660514 }, + { 0x0518, 0x660518 }, + { 0x051c, 0x66051c }, + { 0x052c, 0x66052c }, + { 0x0530, 0x660530 }, + { 0x054c, 0x66054c }, + { 0x0550, 0x660550 }, + { 0x0554, 0x660554 }, + { 0x0558, 0x660558 }, + { 0x055c, 0x66055c }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +gf119_disp_core_mthd = { + .name = "Core", + .addr = 0x000000, + .prev = -0x020000, + .data = { + { "Global", 1, &gf119_disp_core_mthd_base }, + { "DAC", 3, &gf119_disp_core_mthd_dac }, + { "SOR", 8, &gf119_disp_core_mthd_sor }, + { "PIOR", 4, &gf119_disp_core_mthd_pior }, + { "HEAD", 4, &gf119_disp_core_mthd_head }, + {} + } +}; + +void +gf119_disp_core_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + + /* deactivate channel */ + nvkm_mask(device, 0x610490, 0x00000010, 0x00000000); + nvkm_mask(device, 0x610490, 0x00000003, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610490) & 0x001e0000)) + break; + ) < 0) { + nvkm_error(subdev, "core fini: %08x\n", + nvkm_rd32(device, 0x610490)); + } + + chan->suspend_put = nvkm_rd32(device, 0x640000); +} + +static int +gf119_disp_core_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + + /* initialise channel for dma command submission */ + nvkm_wr32(device, 0x610494, chan->push); + nvkm_wr32(device, 0x610498, 0x00010000); + nvkm_wr32(device, 0x61049c, 0x00000001); + nvkm_mask(device, 0x610490, 0x00000010, 0x00000010); + nvkm_wr32(device, 0x640000, chan->suspend_put); + nvkm_wr32(device, 0x610490, 0x01000013); + + /* wait for it to go inactive */ + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610490) & 0x80000000)) + break; + ) < 0) { + nvkm_error(subdev, "core init: %08x\n", + nvkm_rd32(device, 0x610490)); + return -EBUSY; + } + + return 0; +} + +const struct nvkm_disp_chan_func +gf119_disp_core_func = { + .push = nv50_disp_dmac_push, + .init = gf119_disp_core_init, + .fini = gf119_disp_core_fini, + .intr = gf119_disp_chan_intr, + .user = nv50_disp_chan_user, + .bind = gf119_disp_dmac_bind, +}; + +static const struct nvkm_disp_chan_user +gf119_disp_core = { + .func = &gf119_disp_core_func, + .ctrl = 0, + .user = 0, + .mthd = &gf119_disp_core_mthd, +}; + +void +gf119_disp_super(struct work_struct *work) +{ + struct nvkm_disp *disp = container_of(work, struct nvkm_disp, super.work); + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_head *head; + u32 mask[4]; + + nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super.pending)); + mutex_lock(&disp->super.mutex); + + list_for_each_entry(head, &disp->heads, head) { + mask[head->id] = nvkm_rd32(device, 0x6101d4 + (head->id * 0x800)); + HEAD_DBG(head, "%08x", mask[head->id]); + } + + if (disp->super.pending & 0x00000001) { + nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); + nv50_disp_super_1(disp); + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_1_0(disp, head); + } + } else + if (disp->super.pending & 0x00000002) { + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_2_0(disp, head); + } + nvkm_outp_route(disp); + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00010000)) + continue; + nv50_disp_super_2_1(disp, head); + } + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_2_2(disp, head); + } + } else + if (disp->super.pending & 0x00000004) { + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_3_0(disp, head); + } + } + + list_for_each_entry(head, &disp->heads, head) + nvkm_wr32(device, 0x6101d4 + (head->id * 0x800), 0x00000000); + + nvkm_wr32(device, 0x6101d0, 0x80000000); + mutex_unlock(&disp->super.mutex); +} + +void +gf119_disp_intr_error(struct nvkm_disp *disp, int chid) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x6101f0 + (chid * 12)); + u32 type = (stat & 0x00007000) >> 12; + u32 mthd = (stat & 0x00000ffc); + u32 data = nvkm_rd32(device, 0x6101f4 + (chid * 12)); + u32 code = nvkm_rd32(device, 0x6101f8 + (chid * 12)); + const struct nvkm_enum *reason = + nvkm_enum_find(nv50_disp_intr_error_type, type); + + nvkm_error(subdev, "chid %d stat %08x reason %d [%s] mthd %04x " + "data %08x code %08x\n", + chid, stat, type, reason ? reason->name : "", + mthd, data, code); + + if (chid < ARRAY_SIZE(disp->chan)) { + switch (mthd) { + case 0x0080: + nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); + break; + default: + break; + } + } + + nvkm_wr32(device, 0x61009c, (1 << chid)); + nvkm_wr32(device, 0x6101f0 + (chid * 12), 0x90000000); +} + +void +gf119_disp_intr(struct nvkm_disp *disp) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_head *head; + u32 intr = nvkm_rd32(device, 0x610088); + + if (intr & 0x00000001) { + u32 stat = nvkm_rd32(device, 0x61008c); + while (stat) { + int chid = __ffs(stat); stat &= ~(1 << chid); + nv50_disp_chan_uevent_send(disp, chid); + nvkm_wr32(device, 0x61008c, 1 << chid); + } + intr &= ~0x00000001; + } + + if (intr & 0x00000002) { + u32 stat = nvkm_rd32(device, 0x61009c); + int chid = ffs(stat) - 1; + if (chid >= 0) + disp->func->intr_error(disp, chid); + intr &= ~0x00000002; + } + + if (intr & 0x00100000) { + u32 stat = nvkm_rd32(device, 0x6100ac); + if (stat & 0x00000007) { + disp->super.pending = (stat & 0x00000007); + queue_work(disp->super.wq, &disp->super.work); + nvkm_wr32(device, 0x6100ac, disp->super.pending); + stat &= ~0x00000007; + } + + if (stat) { + nvkm_warn(subdev, "intr24 %08x\n", stat); + nvkm_wr32(device, 0x6100ac, stat); + } + + intr &= ~0x00100000; + } + + list_for_each_entry(head, &disp->heads, head) { + const u32 hoff = head->id * 0x800; + u32 mask = 0x01000000 << head->id; + if (mask & intr) { + u32 stat = nvkm_rd32(device, 0x6100bc + hoff); + if (stat & 0x00000001) + nvkm_disp_vblank(disp, head->id); + nvkm_mask(device, 0x6100bc + hoff, 0, 0); + nvkm_rd32(device, 0x6100c0 + hoff); + } + } +} + +void +gf119_disp_fini(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + /* disable all interrupts */ + nvkm_wr32(device, 0x6100b0, 0x00000000); +} + +int +gf119_disp_init(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_head *head; + u32 tmp; + int i; + + /* The below segments of code copying values from one register to + * another appear to inform EVO of the display capabilities or + * something similar. + */ + + /* ... CRTC caps */ + list_for_each_entry(head, &disp->heads, head) { + const u32 hoff = head->id * 0x800; + tmp = nvkm_rd32(device, 0x616104 + hoff); + nvkm_wr32(device, 0x6101b4 + hoff, tmp); + tmp = nvkm_rd32(device, 0x616108 + hoff); + nvkm_wr32(device, 0x6101b8 + hoff, tmp); + tmp = nvkm_rd32(device, 0x61610c + hoff); + nvkm_wr32(device, 0x6101bc + hoff, tmp); + } + + /* ... DAC caps */ + for (i = 0; i < disp->dac.nr; i++) { + tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800)); + nvkm_wr32(device, 0x6101c0 + (i * 0x800), tmp); + } + + /* ... SOR caps */ + for (i = 0; i < disp->sor.nr; i++) { + tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); + nvkm_wr32(device, 0x6301c4 + (i * 0x800), tmp); + } + + /* steal display away from vbios, or something like that */ + if (nvkm_rd32(device, 0x6100ac) & 0x00000100) { + nvkm_wr32(device, 0x6100ac, 0x00000100); + nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002)) + break; + ) < 0) + return -EBUSY; + } + + /* point at display engine memory area (hash table, objects) */ + nvkm_wr32(device, 0x610010, (disp->inst->addr >> 8) | 9); + + /* enable supervisor interrupts, disable everything else */ + nvkm_wr32(device, 0x610090, 0x00000000); + nvkm_wr32(device, 0x6100a0, 0x00000000); + nvkm_wr32(device, 0x6100b0, 0x00000307); + + /* disable underflow reporting, preventing an intermittent issue + * on some gk104 boards where the production vbios left this + * setting enabled by default. + * + * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt + */ + list_for_each_entry(head, &disp->heads, head) { + const u32 hoff = head->id * 0x800; + nvkm_mask(device, 0x616308 + hoff, 0x00000111, 0x00000010); + } + + return 0; +} + +static const struct nvkm_disp_func +gf119_disp = { + .oneinit = nv50_disp_oneinit, + .init = gf119_disp_init, + .fini = gf119_disp_fini, + .intr = gf119_disp_intr, + .intr_error = gf119_disp_intr_error, + .super = gf119_disp_super, + .uevent = &gf119_disp_chan_uevent, + .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, + .dac = { .cnt = gf119_dac_cnt, .new = gf119_dac_new }, + .sor = { .cnt = gf119_sor_cnt, .new = gf119_sor_new }, + .root = { 0,0,GF110_DISP }, + .user = { + {{0,0,GF110_DISP_CURSOR }, nvkm_disp_chan_new, &gf119_disp_curs }, + {{0,0,GF110_DISP_OVERLAY }, nvkm_disp_chan_new, &gf119_disp_oimm }, + {{0,0,GF110_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gf119_disp_base }, + {{0,0,GF110_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gf119_disp_core }, + {{0,0,GF110_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gf119_disp_ovly }, + {} + }, +}; + +int +gf119_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gf119_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c new file mode 100644 index 000000000..7248e9ec8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c @@ -0,0 +1,311 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "hdmi.h" +#include "head.h" +#include "ior.h" + +#include + +void +gk104_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + max_ac_packet << 16 | + rekey; + const u32 hoff = head * 0x800; + const u32 hdmi = head * 0x400; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; + + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); + + if (!(ctrl & 0x40000000)) { + nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000); + nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); + nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); + return; + } + + /* AVI InfoFrame */ + nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); + if (avi_size) { + nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe.header); + nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000); + if (vendor_size) { + nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe.header); + nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001); + } + + + /* ??? InfoFrame? */ + nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); + nvkm_wr32(device, 0x6900cc + hdmi, 0x00000010); + nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000001); + + /* ??? */ + nvkm_wr32(device, 0x690080 + hdmi, 0x82000000); + + /* HDMI_CTRL */ + nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); +} + +static const struct nvkm_ior_func +gk104_sor = { + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gk104_sor_hdmi_ctrl, + }, + .dp = &gf119_sor_dp, + .hda = &gf119_sor_hda, +}; + +int +gk104_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&gk104_sor, disp, SOR, id, true); +} + +static const struct nvkm_disp_mthd_list +gk104_disp_ovly_mthd_base = { + .mthd = 0x0000, + .data = { + { 0x0080, 0x665080 }, + { 0x0084, 0x665084 }, + { 0x0088, 0x665088 }, + { 0x008c, 0x66508c }, + { 0x0090, 0x665090 }, + { 0x0094, 0x665094 }, + { 0x00a0, 0x6650a0 }, + { 0x00a4, 0x6650a4 }, + { 0x00b0, 0x6650b0 }, + { 0x00b4, 0x6650b4 }, + { 0x00b8, 0x6650b8 }, + { 0x00c0, 0x6650c0 }, + { 0x00c4, 0x6650c4 }, + { 0x00e0, 0x6650e0 }, + { 0x00e4, 0x6650e4 }, + { 0x00e8, 0x6650e8 }, + { 0x0100, 0x665100 }, + { 0x0104, 0x665104 }, + { 0x0108, 0x665108 }, + { 0x010c, 0x66510c }, + { 0x0110, 0x665110 }, + { 0x0118, 0x665118 }, + { 0x011c, 0x66511c }, + { 0x0120, 0x665120 }, + { 0x0124, 0x665124 }, + { 0x0130, 0x665130 }, + { 0x0134, 0x665134 }, + { 0x0138, 0x665138 }, + { 0x013c, 0x66513c }, + { 0x0140, 0x665140 }, + { 0x0144, 0x665144 }, + { 0x0148, 0x665148 }, + { 0x014c, 0x66514c }, + { 0x0150, 0x665150 }, + { 0x0154, 0x665154 }, + { 0x0158, 0x665158 }, + { 0x015c, 0x66515c }, + { 0x0160, 0x665160 }, + { 0x0164, 0x665164 }, + { 0x0168, 0x665168 }, + { 0x016c, 0x66516c }, + { 0x0400, 0x665400 }, + { 0x0404, 0x665404 }, + { 0x0408, 0x665408 }, + { 0x040c, 0x66540c }, + { 0x0410, 0x665410 }, + {} + } +}; + +const struct nvkm_disp_chan_mthd +gk104_disp_ovly_mthd = { + .name = "Overlay", + .addr = 0x001000, + .prev = -0x020000, + .data = { + { "Global", 1, &gk104_disp_ovly_mthd_base }, + {} + } +}; + +const struct nvkm_disp_chan_user +gk104_disp_ovly = { + .func = &gf119_disp_dmac_func, + .ctrl = 5, + .user = 5, + .mthd = &gk104_disp_ovly_mthd, +}; + +static const struct nvkm_disp_mthd_list +gk104_disp_core_mthd_head = { + .mthd = 0x0300, + .addr = 0x000300, + .data = { + { 0x0400, 0x660400 }, + { 0x0404, 0x660404 }, + { 0x0408, 0x660408 }, + { 0x040c, 0x66040c }, + { 0x0410, 0x660410 }, + { 0x0414, 0x660414 }, + { 0x0418, 0x660418 }, + { 0x041c, 0x66041c }, + { 0x0420, 0x660420 }, + { 0x0424, 0x660424 }, + { 0x0428, 0x660428 }, + { 0x042c, 0x66042c }, + { 0x0430, 0x660430 }, + { 0x0434, 0x660434 }, + { 0x0438, 0x660438 }, + { 0x0440, 0x660440 }, + { 0x0444, 0x660444 }, + { 0x0448, 0x660448 }, + { 0x044c, 0x66044c }, + { 0x0450, 0x660450 }, + { 0x0454, 0x660454 }, + { 0x0458, 0x660458 }, + { 0x045c, 0x66045c }, + { 0x0460, 0x660460 }, + { 0x0468, 0x660468 }, + { 0x046c, 0x66046c }, + { 0x0470, 0x660470 }, + { 0x0474, 0x660474 }, + { 0x047c, 0x66047c }, + { 0x0480, 0x660480 }, + { 0x0484, 0x660484 }, + { 0x0488, 0x660488 }, + { 0x048c, 0x66048c }, + { 0x0490, 0x660490 }, + { 0x0494, 0x660494 }, + { 0x0498, 0x660498 }, + { 0x04a0, 0x6604a0 }, + { 0x04b0, 0x6604b0 }, + { 0x04b8, 0x6604b8 }, + { 0x04bc, 0x6604bc }, + { 0x04c0, 0x6604c0 }, + { 0x04c4, 0x6604c4 }, + { 0x04c8, 0x6604c8 }, + { 0x04d0, 0x6604d0 }, + { 0x04d4, 0x6604d4 }, + { 0x04e0, 0x6604e0 }, + { 0x04e4, 0x6604e4 }, + { 0x04e8, 0x6604e8 }, + { 0x04ec, 0x6604ec }, + { 0x04f0, 0x6604f0 }, + { 0x04f4, 0x6604f4 }, + { 0x04f8, 0x6604f8 }, + { 0x04fc, 0x6604fc }, + { 0x0500, 0x660500 }, + { 0x0504, 0x660504 }, + { 0x0508, 0x660508 }, + { 0x050c, 0x66050c }, + { 0x0510, 0x660510 }, + { 0x0514, 0x660514 }, + { 0x0518, 0x660518 }, + { 0x051c, 0x66051c }, + { 0x0520, 0x660520 }, + { 0x0524, 0x660524 }, + { 0x052c, 0x66052c }, + { 0x0530, 0x660530 }, + { 0x054c, 0x66054c }, + { 0x0550, 0x660550 }, + { 0x0554, 0x660554 }, + { 0x0558, 0x660558 }, + { 0x055c, 0x66055c }, + {} + } +}; + +const struct nvkm_disp_chan_mthd +gk104_disp_core_mthd = { + .name = "Core", + .addr = 0x000000, + .prev = -0x020000, + .data = { + { "Global", 1, &gf119_disp_core_mthd_base }, + { "DAC", 3, &gf119_disp_core_mthd_dac }, + { "SOR", 8, &gf119_disp_core_mthd_sor }, + { "PIOR", 4, &gf119_disp_core_mthd_pior }, + { "HEAD", 4, &gk104_disp_core_mthd_head }, + {} + } +}; + +const struct nvkm_disp_chan_user +gk104_disp_core = { + .func = &gf119_disp_core_func, + .ctrl = 0, + .user = 0, + .mthd = &gk104_disp_core_mthd, +}; + +static const struct nvkm_disp_func +gk104_disp = { + .oneinit = nv50_disp_oneinit, + .init = gf119_disp_init, + .fini = gf119_disp_fini, + .intr = gf119_disp_intr, + .intr_error = gf119_disp_intr_error, + .super = gf119_disp_super, + .uevent = &gf119_disp_chan_uevent, + .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, + .dac = { .cnt = gf119_dac_cnt, .new = gf119_dac_new }, + .sor = { .cnt = gf119_sor_cnt, .new = gk104_sor_new }, + .root = { 0,0,GK104_DISP }, + .user = { + {{0,0,GK104_DISP_CURSOR }, nvkm_disp_chan_new, &gf119_disp_curs }, + {{0,0,GK104_DISP_OVERLAY }, nvkm_disp_chan_new, &gf119_disp_oimm }, + {{0,0,GK104_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gf119_disp_base }, + {{0,0,GK104_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gk104_disp_core }, + {{0,0,GK104_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gk104_disp_ovly }, + {} + }, +}; + +int +gk104_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gk104_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c new file mode 100644 index 000000000..1704aa381 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c @@ -0,0 +1,59 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +static const struct nvkm_disp_func +gk110_disp = { + .oneinit = nv50_disp_oneinit, + .init = gf119_disp_init, + .fini = gf119_disp_fini, + .intr = gf119_disp_intr, + .intr_error = gf119_disp_intr_error, + .super = gf119_disp_super, + .uevent = &gf119_disp_chan_uevent, + .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, + .dac = { .cnt = gf119_dac_cnt, .new = gf119_dac_new }, + .sor = { .cnt = gf119_sor_cnt, .new = gk104_sor_new }, + .root = { 0,0,GK110_DISP }, + .user = { + {{0,0,GK104_DISP_CURSOR }, nvkm_disp_chan_new, &gf119_disp_curs }, + {{0,0,GK104_DISP_OVERLAY }, nvkm_disp_chan_new, &gf119_disp_oimm }, + {{0,0,GK110_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gf119_disp_base }, + {{0,0,GK110_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gk104_disp_core }, + {{0,0,GK104_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gk104_disp_ovly }, + {} + }, +}; + +int +gk110_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gk110_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c new file mode 100644 index 000000000..9e9ef49bd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c @@ -0,0 +1,114 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +void +gm107_sor_dp_pattern(struct nvkm_ior *sor, int pattern) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + u32 mask = 0x1f1f1f1f, data; + + switch (pattern) { + case 0: data = 0x10101010; break; + case 1: data = 0x01010101; break; + case 2: data = 0x02020202; break; + case 3: data = 0x03030303; break; + case 4: data = 0x1b1b1b1b; break; + default: + WARN_ON(1); + return; + } + + if (sor->asy.link & 1) + nvkm_mask(device, 0x61c110 + soff, mask, data); + else + nvkm_mask(device, 0x61c12c + soff, mask, data); +} + +static const struct nvkm_ior_func_dp +gm107_sor_dp = { + .lanes = { 0, 1, 2, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gm107_sor_dp_pattern, + .drive = gf119_sor_dp_drive, + .vcpi = gf119_sor_dp_vcpi, + .audio = gf119_sor_dp_audio, + .audio_sym = gf119_sor_dp_audio_sym, + .watermark = gf119_sor_dp_watermark, +}; + +static const struct nvkm_ior_func +gm107_sor = { + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gk104_sor_hdmi_ctrl, + }, + .dp = &gm107_sor_dp, + .hda = &gf119_sor_hda, +}; + +static int +gm107_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&gm107_sor, disp, SOR, id, true); +} + +static const struct nvkm_disp_func +gm107_disp = { + .oneinit = nv50_disp_oneinit, + .init = gf119_disp_init, + .fini = gf119_disp_fini, + .intr = gf119_disp_intr, + .intr_error = gf119_disp_intr_error, + .super = gf119_disp_super, + .uevent = &gf119_disp_chan_uevent, + .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, + .dac = { .cnt = gf119_dac_cnt, .new = gf119_dac_new }, + .sor = { .cnt = gf119_sor_cnt, .new = gm107_sor_new }, + .root = { 0,0,GM107_DISP }, + .user = { + {{0,0,GK104_DISP_CURSOR }, nvkm_disp_chan_new, &gf119_disp_curs }, + {{0,0,GK104_DISP_OVERLAY }, nvkm_disp_chan_new, &gf119_disp_oimm }, + {{0,0,GK110_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gf119_disp_base }, + {{0,0,GM107_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gk104_disp_core }, + {{0,0,GK104_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gk104_disp_ovly }, + {} + }, +}; + +int +gm107_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gm107_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c new file mode 100644 index 000000000..4ecc8f98a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c @@ -0,0 +1,182 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "hdmi.h" +#include "head.h" +#include "ior.h" +#include "outp.h" + +#include + +void +gm200_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + const u32 shift = sor->func->dp->lanes[ln] * 8; + u32 data[4]; + + pu &= 0x0f; + + data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nvkm_rd32(device, 0x61c130 + loff); + if ((data[2] & 0x00000f00) < (pu << 8) || ln == 0) + data[2] = (data[2] & ~0x00000f00) | (pu << 8); + + nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); + nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); + nvkm_wr32(device, 0x61c130 + loff, data[2]); + + data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); + nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); +} + +const struct nvkm_ior_func_dp +gm200_sor_dp = { + .lanes = { 0, 1, 2, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gm107_sor_dp_pattern, + .drive = gm200_sor_dp_drive, + .vcpi = gf119_sor_dp_vcpi, + .audio = gf119_sor_dp_audio, + .audio_sym = gf119_sor_dp_audio_sym, + .watermark = gf119_sor_dp_watermark, +}; + +void +gm200_sor_hdmi_scdc(struct nvkm_ior *ior, u8 scdc) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + const u32 ctrl = scdc & 0x3; + + nvkm_mask(device, 0x61c5bc + soff, 0x00000003, ctrl); + + ior->tmds.high_speed = !!(scdc & 0x2); +} + +void +gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior) +{ + struct nvkm_device *device = outp->disp->engine.subdev.device; + const u32 moff = __ffs(outp->info.or) * 0x100; + const u32 sor = ior ? ior->id + 1 : 0; + u32 link = ior ? (ior->asy.link == 2) : 0; + + if (outp->info.sorconf.link & 1) { + nvkm_mask(device, 0x612308 + moff, 0x0000001f, link << 4 | sor); + link++; + } + + if (outp->info.sorconf.link & 2) + nvkm_mask(device, 0x612388 + moff, 0x0000001f, link << 4 | sor); +} + +int +gm200_sor_route_get(struct nvkm_outp *outp, int *link) +{ + struct nvkm_device *device = outp->disp->engine.subdev.device; + const int sublinks = outp->info.sorconf.link; + int lnk[2], sor[2], m, s; + + for (*link = 0, m = __ffs(outp->info.or) * 2, s = 0; s < 2; m++, s++) { + if (sublinks & BIT(s)) { + u32 data = nvkm_rd32(device, 0x612308 + (m * 0x80)); + lnk[s] = (data & 0x00000010) >> 4; + sor[s] = (data & 0x0000000f); + if (!sor[s]) + return -1; + *link |= lnk[s]; + } + } + + if (sublinks == 3) { + if (sor[0] != sor[1] || WARN_ON(lnk[0] || !lnk[1])) + return -1; + } + + return ((sublinks & 1) ? sor[0] : sor[1]) - 1; +} + +static const struct nvkm_ior_func +gm200_sor = { + .route = { + .get = gm200_sor_route_get, + .set = gm200_sor_route_set, + }, + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gk104_sor_hdmi_ctrl, + .scdc = gm200_sor_hdmi_scdc, + }, + .dp = &gm200_sor_dp, + .hda = &gf119_sor_hda, +}; + +static int +gm200_sor_new(struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + u32 hda; + + if (!((hda = nvkm_rd32(device, 0x08a15c)) & 0x40000000)) + hda = nvkm_rd32(device, 0x101034); + + return nvkm_ior_new_(&gm200_sor, disp, SOR, id, hda & BIT(id)); +} + +static const struct nvkm_disp_func +gm200_disp = { + .oneinit = nv50_disp_oneinit, + .init = gf119_disp_init, + .fini = gf119_disp_fini, + .intr = gf119_disp_intr, + .intr_error = gf119_disp_intr_error, + .super = gf119_disp_super, + .uevent = &gf119_disp_chan_uevent, + .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, + .dac = { .cnt = gf119_dac_cnt, .new = gf119_dac_new }, + .sor = { .cnt = gf119_sor_cnt, .new = gm200_sor_new }, + .root = { 0,0,GM200_DISP }, + .user = { + {{0,0,GK104_DISP_CURSOR }, nvkm_disp_chan_new, &gf119_disp_curs }, + {{0,0,GK104_DISP_OVERLAY }, nvkm_disp_chan_new, &gf119_disp_oimm }, + {{0,0,GK110_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gf119_disp_base }, + {{0,0,GM200_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gk104_disp_core }, + {{0,0,GK104_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gk104_disp_ovly }, + {} + }, +}; + +int +gm200_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gm200_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c new file mode 100644 index 000000000..7172a9dfd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c @@ -0,0 +1,87 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +static const struct nvkm_ior_func +gp100_sor = { + .route = { + .get = gm200_sor_route_get, + .set = gm200_sor_route_set, + }, + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gk104_sor_hdmi_ctrl, + .scdc = gm200_sor_hdmi_scdc, + }, + .dp = &gm200_sor_dp, + .hda = &gf119_sor_hda, +}; + +int +gp100_sor_new(struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + u32 hda; + + if (!((hda = nvkm_rd32(device, 0x08a15c)) & 0x40000000)) + hda = nvkm_rd32(device, 0x10ebb0) >> 8; + + return nvkm_ior_new_(&gp100_sor, disp, SOR, id, hda & BIT(id)); +} + +static const struct nvkm_disp_func +gp100_disp = { + .oneinit = nv50_disp_oneinit, + .init = gf119_disp_init, + .fini = gf119_disp_fini, + .intr = gf119_disp_intr, + .intr_error = gf119_disp_intr_error, + .super = gf119_disp_super, + .uevent = &gf119_disp_chan_uevent, + .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, + .sor = { .cnt = gf119_sor_cnt, .new = gp100_sor_new }, + .root = { 0,0,GP100_DISP }, + .user = { + {{0,0,GK104_DISP_CURSOR }, nvkm_disp_chan_new, &gf119_disp_curs }, + {{0,0,GK104_DISP_OVERLAY }, nvkm_disp_chan_new, &gf119_disp_oimm }, + {{0,0,GK110_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gf119_disp_base }, + {{0,0,GP100_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gk104_disp_core }, + {{0,0,GK104_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gk104_disp_ovly }, + {} + }, +}; + +int +gp100_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gp100_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c new file mode 100644 index 000000000..07e9aeec5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c @@ -0,0 +1,200 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +#include + +static int +gp102_disp_dmac_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + /* initialise channel for dma command submission */ + nvkm_wr32(device, 0x611494 + (ctrl * 0x0010), chan->push); + nvkm_wr32(device, 0x611498 + (ctrl * 0x0010), 0x00010000); + nvkm_wr32(device, 0x61149c + (ctrl * 0x0010), 0x00000001); + nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000010, 0x00000010); + nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put); + nvkm_wr32(device, 0x610490 + (ctrl * 0x0010), 0x00000013); + + /* wait for it to go inactive */ + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610490 + (ctrl * 0x10)) & 0x80000000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d init: %08x\n", user, + nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); + return -EBUSY; + } + + return 0; +} + +const struct nvkm_disp_chan_func +gp102_disp_dmac_func = { + .push = nv50_disp_dmac_push, + .init = gp102_disp_dmac_init, + .fini = gf119_disp_dmac_fini, + .intr = gf119_disp_chan_intr, + .user = nv50_disp_chan_user, + .bind = gf119_disp_dmac_bind, +}; + +static const struct nvkm_disp_chan_user +gp102_disp_curs = { + .func = &gf119_disp_pioc_func, + .ctrl = 13, + .user = 17, +}; + +static const struct nvkm_disp_chan_user +gp102_disp_oimm = { + .func = &gf119_disp_pioc_func, + .ctrl = 9, + .user = 13, +}; + +static const struct nvkm_disp_chan_user +gp102_disp_ovly = { + .func = &gp102_disp_dmac_func, + .ctrl = 5, + .user = 5, + .mthd = &gk104_disp_ovly_mthd, +}; + +static const struct nvkm_disp_chan_user +gp102_disp_base = { + .func = &gp102_disp_dmac_func, + .ctrl = 1, + .user = 1, + .mthd = &gf119_disp_base_mthd, +}; + +static int +gp102_disp_core_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + + /* initialise channel for dma command submission */ + nvkm_wr32(device, 0x611494, chan->push); + nvkm_wr32(device, 0x611498, 0x00010000); + nvkm_wr32(device, 0x61149c, 0x00000001); + nvkm_mask(device, 0x610490, 0x00000010, 0x00000010); + nvkm_wr32(device, 0x640000, chan->suspend_put); + nvkm_wr32(device, 0x610490, 0x01000013); + + /* wait for it to go inactive */ + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610490) & 0x80000000)) + break; + ) < 0) { + nvkm_error(subdev, "core init: %08x\n", + nvkm_rd32(device, 0x610490)); + return -EBUSY; + } + + return 0; +} + +static const struct nvkm_disp_chan_func +gp102_disp_core_func = { + .push = nv50_disp_dmac_push, + .init = gp102_disp_core_init, + .fini = gf119_disp_core_fini, + .intr = gf119_disp_chan_intr, + .user = nv50_disp_chan_user, + .bind = gf119_disp_dmac_bind, +}; + +static const struct nvkm_disp_chan_user +gp102_disp_core = { + .func = &gp102_disp_core_func, + .ctrl = 0, + .user = 0, + .mthd = &gk104_disp_core_mthd, +}; + +static void +gp102_disp_intr_error(struct nvkm_disp *disp, int chid) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mthd = nvkm_rd32(device, 0x6111f0 + (chid * 12)); + u32 data = nvkm_rd32(device, 0x6111f4 + (chid * 12)); + u32 unkn = nvkm_rd32(device, 0x6111f8 + (chid * 12)); + + nvkm_error(subdev, "chid %d mthd %04x data %08x %08x %08x\n", + chid, (mthd & 0x0000ffc), data, mthd, unkn); + + if (chid < ARRAY_SIZE(disp->chan)) { + switch (mthd & 0xffc) { + case 0x0080: + nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); + break; + default: + break; + } + } + + nvkm_wr32(device, 0x61009c, (1 << chid)); + nvkm_wr32(device, 0x6111f0 + (chid * 12), 0x90000000); +} + +static const struct nvkm_disp_func +gp102_disp = { + .oneinit = nv50_disp_oneinit, + .init = gf119_disp_init, + .fini = gf119_disp_fini, + .intr = gf119_disp_intr, + .intr_error = gp102_disp_intr_error, + .super = gf119_disp_super, + .uevent = &gf119_disp_chan_uevent, + .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, + .sor = { .cnt = gf119_sor_cnt, .new = gp100_sor_new }, + .root = { 0,0,GP102_DISP }, + .user = { + {{0,0,GK104_DISP_CURSOR }, nvkm_disp_chan_new, &gp102_disp_curs }, + {{0,0,GK104_DISP_OVERLAY }, nvkm_disp_chan_new, &gp102_disp_oimm }, + {{0,0,GK110_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gp102_disp_base }, + {{0,0,GP102_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gp102_disp_core }, + {{0,0,GK104_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gp102_disp_ovly }, + {} + }, +}; + +int +gp102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gp102_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c new file mode 100644 index 000000000..6f69c4e3a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c @@ -0,0 +1,109 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +static const struct nvkm_disp_mthd_list +gt200_disp_ovly_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x000000 }, + { 0x0084, 0x6109a0 }, + { 0x0088, 0x6109c0 }, + { 0x008c, 0x6109c8 }, + { 0x0090, 0x6109b4 }, + { 0x0094, 0x610970 }, + { 0x00a0, 0x610998 }, + { 0x00a4, 0x610964 }, + { 0x00b0, 0x610c98 }, + { 0x00b4, 0x610ca4 }, + { 0x00b8, 0x610cac }, + { 0x00c0, 0x610958 }, + { 0x00e0, 0x6109a8 }, + { 0x00e4, 0x6109d0 }, + { 0x00e8, 0x6109d8 }, + { 0x0100, 0x61094c }, + { 0x0104, 0x610984 }, + { 0x0108, 0x61098c }, + { 0x0800, 0x6109f8 }, + { 0x0808, 0x610a08 }, + { 0x080c, 0x610a10 }, + { 0x0810, 0x610a00 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +gt200_disp_ovly_mthd = { + .name = "Overlay", + .addr = 0x000540, + .prev = 0x000004, + .data = { + { "Global", 1, >200_disp_ovly_mthd_base }, + {} + } +}; + +const struct nvkm_disp_chan_user +gt200_disp_ovly = { + .func = &nv50_disp_dmac_func, + .ctrl = 3, + .user = 3, + .mthd = >200_disp_ovly_mthd, +}; + +static const struct nvkm_disp_func +gt200_disp = { + .oneinit = nv50_disp_oneinit, + .init = nv50_disp_init, + .fini = nv50_disp_fini, + .intr = nv50_disp_intr, + .super = nv50_disp_super, + .uevent = &nv50_disp_chan_uevent, + .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, + .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, + .sor = { .cnt = nv50_sor_cnt, .new = g84_sor_new }, + .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, + .root = { 0,0,GT200_DISP }, + .user = { + {{0,0, G82_DISP_CURSOR }, nvkm_disp_chan_new, & nv50_disp_curs }, + {{0,0, G82_DISP_OVERLAY }, nvkm_disp_chan_new, & nv50_disp_oimm }, + {{0,0,GT200_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, & g84_disp_base }, + {{0,0,GT200_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, & g84_disp_core }, + {{0,0,GT200_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, >200_disp_ovly }, + {} + }, +}; + +int +gt200_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(>200_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c new file mode 100644 index 000000000..70c49e7af --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c @@ -0,0 +1,208 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "hdmi.h" +#include "head.h" +#include "ior.h" + +#include + +#include + +static void +gt215_sor_hda_eld(struct nvkm_ior *ior, int head, u8 *data, u8 size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = ior->id * 0x800; + int i; + + for (i = 0; i < size; i++) + nvkm_wr32(device, 0x61c440 + soff, (i << 8) | data[i]); + for (; i < 0x60; i++) + nvkm_wr32(device, 0x61c440 + soff, (i << 8)); + nvkm_mask(device, 0x61c448 + soff, 0x80000002, 0x80000002); +} + +static void +gt215_sor_hda_hpd(struct nvkm_ior *ior, int head, bool present) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + u32 data = 0x80000000; + u32 mask = 0x80000001; + if (present) + data |= 0x00000001; + else + mask |= 0x00000002; + nvkm_mask(device, 0x61c448 + ior->id * 0x800, mask, data); +} + +const struct nvkm_ior_func_hda +gt215_sor_hda = { + .hpd = gt215_sor_hda_hpd, + .eld = gt215_sor_hda_eld, +}; + +void +gt215_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 data = 0x80000000 | (0x00000001 * enable); + const u32 mask = 0x8000000d; + + nvkm_mask(device, 0x61c1e0 + soff, mask, data); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c1e0 + soff) & 0x80000000)) + break; + ); +} + +static const struct nvkm_ior_func_dp +gt215_sor_dp = { + .lanes = { 2, 1, 0, 3 }, + .links = g94_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = g94_sor_dp_pattern, + .drive = g94_sor_dp_drive, + .audio = gt215_sor_dp_audio, + .audio_sym = g94_sor_dp_audio_sym, + .activesym = g94_sor_dp_activesym, + .watermark = g94_sor_dp_watermark, +}; + +void +gt215_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + 0x1f000000 /* ??? */ | + max_ac_packet << 16 | + rekey; + const u32 soff = nv50_ior_base(ior); + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; + + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); + + if (!(ctrl & 0x40000000)) { + nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); + return; + } + + /* AVI InfoFrame */ + nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); + if (avi_size) { + nvkm_wr32(device, 0x61c528 + soff, avi_infoframe.header); + nvkm_wr32(device, 0x61c52c + soff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c530 + soff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x61c534 + soff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61c538 + soff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + } + + /* Audio InfoFrame */ + nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); + nvkm_wr32(device, 0x61c508 + soff, 0x000a0184); + nvkm_wr32(device, 0x61c50c + soff, 0x00000071); + nvkm_wr32(device, 0x61c510 + soff, 0x00000000); + nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); + + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000); + if (vendor_size) { + nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe.header); + nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe.subpack1_low); */ + /* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe.subpack1_high); */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001); + } + + nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ + nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ + nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ + + /* ??? */ + nvkm_mask(device, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nvkm_mask(device, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nvkm_mask(device, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* HDMI_CTRL */ + nvkm_mask(device, 0x61c5a4 + soff, 0x5f1f007f, ctrl); +} + +static const struct nvkm_ior_func +gt215_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = gt215_sor_hdmi_ctrl, + }, + .dp = >215_sor_dp, + .hda = >215_sor_hda, +}; + +static int +gt215_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(>215_sor, disp, SOR, id, true); +} + +static const struct nvkm_disp_func +gt215_disp = { + .oneinit = nv50_disp_oneinit, + .init = nv50_disp_init, + .fini = nv50_disp_fini, + .intr = nv50_disp_intr, + .super = nv50_disp_super, + .uevent = &nv50_disp_chan_uevent, + .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, + .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, + .sor = { .cnt = g94_sor_cnt, .new = gt215_sor_new }, + .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, + .root = { 0,0,GT214_DISP }, + .user = { + {{0,0,GT214_DISP_CURSOR }, nvkm_disp_chan_new, & nv50_disp_curs }, + {{0,0,GT214_DISP_OVERLAY }, nvkm_disp_chan_new, & nv50_disp_oimm }, + {{0,0,GT214_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, & g84_disp_base }, + {{0,0,GT214_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, & g94_disp_core }, + {{0,0,GT214_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, & g84_disp_ovly }, + {} + }, +}; + +int +gt215_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(>215_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c new file mode 100644 index 000000000..6b9d49270 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c @@ -0,0 +1,1235 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "chan.h" +#include "hdmi.h" +#include "head.h" +#include "ior.h" +#include "outp.h" + +#include +#include +#include +#include + +#include +#include + +static void +gv100_sor_hda_device_entry(struct nvkm_ior *ior, int head) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 hoff = 0x800 * head; + + nvkm_mask(device, 0x616528 + hoff, 0x00000070, head << 4); +} + +const struct nvkm_ior_func_hda +gv100_sor_hda = { + .hpd = gf119_sor_hda_hpd, + .eld = gf119_sor_hda_eld, + .device_entry = gv100_sor_hda_device_entry, +}; + +void +gv100_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + + nvkm_mask(device, 0x616550 + hoff, 0x0c00003f, 0x08000000 | watermark); +} + +void +gv100_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + + nvkm_mask(device, 0x616568 + hoff, 0x0000ffff, h); + nvkm_mask(device, 0x61656c + hoff, 0x00ffffff, v); +} + +void +gv100_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = 0x800 * head; + const u32 data = 0x80000000 | (0x00000001 * enable); + const u32 mask = 0x8000000d; + + nvkm_mask(device, 0x616560 + hoff, mask, data); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x616560 + hoff) & 0x80000000)) + break; + ); +} + +static const struct nvkm_ior_func_dp +gv100_sor_dp = { + .lanes = { 0, 1, 2, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gm107_sor_dp_pattern, + .drive = gm200_sor_dp_drive, + .audio = gv100_sor_dp_audio, + .audio_sym = gv100_sor_dp_audio_sym, + .watermark = gv100_sor_dp_watermark, +}; + +void +gv100_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + max_ac_packet << 16 | + rekey; + const u32 hoff = head * 0x800; + const u32 hdmi = head * 0x400; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; + + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); + + if (!(ctrl & 0x40000000)) { + nvkm_mask(device, 0x6165c0 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x6f0100 + hdmi, 0x00000001, 0x00000000); + nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000); + nvkm_mask(device, 0x6f0000 + hdmi, 0x00000001, 0x00000000); + return; + } + + /* AVI InfoFrame (AVI). */ + nvkm_mask(device, 0x6f0000 + hdmi, 0x00000001, 0x00000000); + if (avi_size) { + nvkm_wr32(device, 0x6f0008 + hdmi, avi_infoframe.header); + nvkm_wr32(device, 0x6f000c + hdmi, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x6f0010 + hdmi, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x6f0014 + hdmi, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x6f0018 + hdmi, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x6f0000 + hdmi, 0x00000001, 0x00000001); + } + + /* Vendor-specific InfoFrame (VSI). */ + nvkm_mask(device, 0x6f0100 + hdmi, 0x00010001, 0x00000000); + if (vendor_size) { + nvkm_wr32(device, 0x6f0108 + hdmi, vendor_infoframe.header); + nvkm_wr32(device, 0x6f010c + hdmi, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x6f0110 + hdmi, vendor_infoframe.subpack0_high); + nvkm_wr32(device, 0x6f0114 + hdmi, 0x00000000); + nvkm_wr32(device, 0x6f0118 + hdmi, 0x00000000); + nvkm_wr32(device, 0x6f011c + hdmi, 0x00000000); + nvkm_wr32(device, 0x6f0120 + hdmi, 0x00000000); + nvkm_wr32(device, 0x6f0124 + hdmi, 0x00000000); + nvkm_mask(device, 0x6f0100 + hdmi, 0x00000001, 0x00000001); + } + + + /* General Control (GCP). */ + nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000); + nvkm_wr32(device, 0x6f00cc + hdmi, 0x00000010); + nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001); + + /* Audio Clock Regeneration (ACR). */ + nvkm_wr32(device, 0x6f0080 + hdmi, 0x82000000); + + /* NV_PDISP_SF_HDMI_CTRL. */ + nvkm_mask(device, 0x6165c0 + hoff, 0x401f007f, ctrl); +} + +void +gv100_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 coff = (state == &sor->arm) * 0x8000 + sor->id * 0x20; + u32 ctrl = nvkm_rd32(device, 0x680300 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = LVDS; state->link = 1; break; + case 1: state->proto = TMDS; state->link = 1; break; + case 2: state->proto = TMDS; state->link = 2; break; + case 5: state->proto = TMDS; state->link = 3; break; + case 8: state->proto = DP; state->link = 1; break; + case 9: state->proto = DP; state->link = 2; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x000000ff; +} + +static const struct nvkm_ior_func +gv100_sor = { + .route = { + .get = gm200_sor_route_get, + .set = gm200_sor_route_set, + }, + .state = gv100_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gv100_sor_hdmi_ctrl, + .scdc = gm200_sor_hdmi_scdc, + }, + .dp = &gv100_sor_dp, + .hda = &gv100_sor_hda, +}; + +static int +gv100_sor_new(struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + u32 hda; + + if (!((hda = nvkm_rd32(device, 0x08a15c)) & 0x40000000)) + hda = nvkm_rd32(device, 0x118fb0) >> 8; + + return nvkm_ior_new_(&gv100_sor, disp, SOR, id, hda & BIT(id)); +} + +int +gv100_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + *pmask = (nvkm_rd32(device, 0x610060) & 0x0000ff00) >> 8; + return (nvkm_rd32(device, 0x610074) & 0x00000f00) >> 8; +} + +static void +gv100_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000004, 0x00000000); +} + +static void +gv100_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000004, 0x00000004); +} + +static void +gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x800; + /* vline read locks hline. */ + *vline = nvkm_rd32(device, 0x616330 + hoff) & 0x0000ffff; + *hline = nvkm_rd32(device, 0x616334 + hoff) & 0x0000ffff; +} + +static void +gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = (state == &head->arm) * 0x8000 + head->id * 0x400; + u32 data; + + data = nvkm_rd32(device, 0x682064 + hoff); + state->vtotal = (data & 0xffff0000) >> 16; + state->htotal = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x682068 + hoff); + state->vsynce = (data & 0xffff0000) >> 16; + state->hsynce = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x68206c + hoff); + state->vblanke = (data & 0xffff0000) >> 16; + state->hblanke = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x682070 + hoff); + state->vblanks = (data & 0xffff0000) >> 16; + state->hblanks = (data & 0x0000ffff); + state->hz = nvkm_rd32(device, 0x68200c + hoff); + + data = nvkm_rd32(device, 0x682004 + hoff); + switch ((data & 0x000000f0) >> 4) { + case 5: state->or.depth = 30; break; + case 4: state->or.depth = 24; break; + case 1: state->or.depth = 18; break; + default: + state->or.depth = 18; + WARN_ON(1); + break; + } +} + +static const struct nvkm_head_func +gv100_head = { + .state = gv100_head_state, + .rgpos = gv100_head_rgpos, + .rgclk = gf119_head_rgclk, + .vblank_get = gv100_head_vblank_get, + .vblank_put = gv100_head_vblank_put, +}; + +int +gv100_head_new(struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + if (!(nvkm_rd32(device, 0x610060) & (0x00000001 << id))) + return 0; + + return nvkm_head_new_(&gv100_head, disp, id); +} + +int +gv100_head_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + *pmask = nvkm_rd32(device, 0x610060) & 0x000000ff; + return nvkm_rd32(device, 0x610074) & 0x0000000f; +} + +const struct nvkm_event_func +gv100_disp_chan_uevent = { +}; + +u64 +gv100_disp_chan_user(struct nvkm_disp_chan *chan, u64 *psize) +{ + *psize = 0x1000; + return 0x690000 + ((chan->chid.user - 1) * 0x1000); +} + +static int +gv100_disp_dmac_idle(struct nvkm_disp_chan *chan) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 soff = (chan->chid.ctrl - 1) * 0x04; + nvkm_msec(device, 2000, + u32 stat = nvkm_rd32(device, 0x610664 + soff); + if ((stat & 0x000f0000) == 0x00040000) + return 0; + ); + return -EBUSY; +} + +int +gv100_disp_dmac_bind(struct nvkm_disp_chan *chan, + struct nvkm_object *object, u32 handle) +{ + return nvkm_ramht_insert(chan->disp->ramht, object, chan->chid.user, -9, handle, + chan->chid.user << 25 | 0x00000040); +} + +void +gv100_disp_dmac_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 uoff = (chan->chid.ctrl - 1) * 0x1000; + const u32 coff = chan->chid.ctrl * 0x04; + nvkm_mask(device, 0x6104e0 + coff, 0x00000010, 0x00000000); + gv100_disp_dmac_idle(chan); + nvkm_mask(device, 0x6104e0 + coff, 0x00000002, 0x00000000); + chan->suspend_put = nvkm_rd32(device, 0x690000 + uoff); +} + +int +gv100_disp_dmac_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 uoff = (chan->chid.ctrl - 1) * 0x1000; + const u32 poff = chan->chid.ctrl * 0x10; + const u32 coff = chan->chid.ctrl * 0x04; + + nvkm_wr32(device, 0x610b24 + poff, lower_32_bits(chan->push)); + nvkm_wr32(device, 0x610b20 + poff, upper_32_bits(chan->push)); + nvkm_wr32(device, 0x610b28 + poff, 0x00000001); + nvkm_wr32(device, 0x610b2c + poff, 0x00000040); + + nvkm_mask(device, 0x6104e0 + coff, 0x00000010, 0x00000010); + nvkm_wr32(device, 0x690000 + uoff, chan->suspend_put); + nvkm_wr32(device, 0x6104e0 + coff, 0x00000013); + return gv100_disp_dmac_idle(chan); +} + +static void +gv100_disp_wimm_intr(struct nvkm_disp_chan *chan, bool en) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 mask = 0x00000001 << chan->head; + const u32 data = en ? mask : 0; + nvkm_mask(device, 0x611da8, mask, data); +} + +static const struct nvkm_disp_chan_func +gv100_disp_wimm_func = { + .push = nv50_disp_dmac_push, + .init = gv100_disp_dmac_init, + .fini = gv100_disp_dmac_fini, + .intr = gv100_disp_wimm_intr, + .user = gv100_disp_chan_user, +}; + +const struct nvkm_disp_chan_user +gv100_disp_wimm = { + .func = &gv100_disp_wimm_func, + .ctrl = 33, + .user = 33, +}; + +static const struct nvkm_disp_mthd_list +gv100_disp_wndw_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0200, 0x690200 }, + { 0x020c, 0x69020c }, + { 0x0210, 0x690210 }, + { 0x0214, 0x690214 }, + { 0x0218, 0x690218 }, + { 0x021c, 0x69021c }, + { 0x0220, 0x690220 }, + { 0x0224, 0x690224 }, + { 0x0228, 0x690228 }, + { 0x022c, 0x69022c }, + { 0x0230, 0x690230 }, + { 0x0234, 0x690234 }, + { 0x0238, 0x690238 }, + { 0x0240, 0x690240 }, + { 0x0244, 0x690244 }, + { 0x0248, 0x690248 }, + { 0x024c, 0x69024c }, + { 0x0250, 0x690250 }, + { 0x0254, 0x690254 }, + { 0x0260, 0x690260 }, + { 0x0264, 0x690264 }, + { 0x0268, 0x690268 }, + { 0x026c, 0x69026c }, + { 0x0270, 0x690270 }, + { 0x0274, 0x690274 }, + { 0x0280, 0x690280 }, + { 0x0284, 0x690284 }, + { 0x0288, 0x690288 }, + { 0x028c, 0x69028c }, + { 0x0290, 0x690290 }, + { 0x0298, 0x690298 }, + { 0x029c, 0x69029c }, + { 0x02a0, 0x6902a0 }, + { 0x02a4, 0x6902a4 }, + { 0x02a8, 0x6902a8 }, + { 0x02ac, 0x6902ac }, + { 0x02b0, 0x6902b0 }, + { 0x02b4, 0x6902b4 }, + { 0x02b8, 0x6902b8 }, + { 0x02bc, 0x6902bc }, + { 0x02c0, 0x6902c0 }, + { 0x02c4, 0x6902c4 }, + { 0x02c8, 0x6902c8 }, + { 0x02cc, 0x6902cc }, + { 0x02d0, 0x6902d0 }, + { 0x02d4, 0x6902d4 }, + { 0x02d8, 0x6902d8 }, + { 0x02dc, 0x6902dc }, + { 0x02e0, 0x6902e0 }, + { 0x02e4, 0x6902e4 }, + { 0x02e8, 0x6902e8 }, + { 0x02ec, 0x6902ec }, + { 0x02f0, 0x6902f0 }, + { 0x02f4, 0x6902f4 }, + { 0x02f8, 0x6902f8 }, + { 0x02fc, 0x6902fc }, + { 0x0300, 0x690300 }, + { 0x0304, 0x690304 }, + { 0x0308, 0x690308 }, + { 0x0310, 0x690310 }, + { 0x0314, 0x690314 }, + { 0x0318, 0x690318 }, + { 0x031c, 0x69031c }, + { 0x0320, 0x690320 }, + { 0x0324, 0x690324 }, + { 0x0328, 0x690328 }, + { 0x032c, 0x69032c }, + { 0x033c, 0x69033c }, + { 0x0340, 0x690340 }, + { 0x0344, 0x690344 }, + { 0x0348, 0x690348 }, + { 0x034c, 0x69034c }, + { 0x0350, 0x690350 }, + { 0x0354, 0x690354 }, + { 0x0358, 0x690358 }, + { 0x0364, 0x690364 }, + { 0x0368, 0x690368 }, + { 0x036c, 0x69036c }, + { 0x0370, 0x690370 }, + { 0x0374, 0x690374 }, + { 0x0380, 0x690380 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +gv100_disp_wndw_mthd = { + .name = "Window", + .addr = 0x001000, + .prev = 0x000800, + .data = { + { "Global", 1, &gv100_disp_wndw_mthd_base }, + {} + } +}; + +static void +gv100_disp_wndw_intr(struct nvkm_disp_chan *chan, bool en) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 mask = 0x00000001 << chan->head; + const u32 data = en ? mask : 0; + nvkm_mask(device, 0x611da4, mask, data); +} + +static const struct nvkm_disp_chan_func +gv100_disp_wndw_func = { + .push = nv50_disp_dmac_push, + .init = gv100_disp_dmac_init, + .fini = gv100_disp_dmac_fini, + .intr = gv100_disp_wndw_intr, + .user = gv100_disp_chan_user, + .bind = gv100_disp_dmac_bind, +}; + +const struct nvkm_disp_chan_user +gv100_disp_wndw = { + .func = &gv100_disp_wndw_func, + .ctrl = 1, + .user = 1, + .mthd = &gv100_disp_wndw_mthd, +}; + +int +gv100_disp_wndw_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + *pmask = nvkm_rd32(device, 0x610064); + return (nvkm_rd32(device, 0x610074) & 0x03f00000) >> 20; +} + +static int +gv100_disp_curs_idle(struct nvkm_disp_chan *chan) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 soff = (chan->chid.ctrl - 1) * 0x04; + nvkm_msec(device, 2000, + u32 stat = nvkm_rd32(device, 0x610664 + soff); + if ((stat & 0x00070000) == 0x00040000) + return 0; + ); + return -EBUSY; +} + +static void +gv100_disp_curs_intr(struct nvkm_disp_chan *chan, bool en) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 mask = 0x00010000 << chan->head; + const u32 data = en ? mask : 0; + nvkm_mask(device, 0x611dac, mask, data); +} + +static void +gv100_disp_curs_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 hoff = chan->chid.ctrl * 4; + nvkm_mask(device, 0x6104e0 + hoff, 0x00000010, 0x00000010); + gv100_disp_curs_idle(chan); + nvkm_mask(device, 0x6104e0 + hoff, 0x00000001, 0x00000000); +} + +static int +gv100_disp_curs_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + nvkm_wr32(device, 0x6104e0 + chan->chid.ctrl * 4, 0x00000001); + return gv100_disp_curs_idle(chan); +} + +static const struct nvkm_disp_chan_func +gv100_disp_curs_func = { + .init = gv100_disp_curs_init, + .fini = gv100_disp_curs_fini, + .intr = gv100_disp_curs_intr, + .user = gv100_disp_chan_user, +}; + +const struct nvkm_disp_chan_user +gv100_disp_curs = { + .func = &gv100_disp_curs_func, + .ctrl = 73, + .user = 73, +}; + +const struct nvkm_disp_mthd_list +gv100_disp_core_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0200, 0x680200 }, + { 0x0208, 0x680208 }, + { 0x020c, 0x68020c }, + { 0x0210, 0x680210 }, + { 0x0214, 0x680214 }, + { 0x0218, 0x680218 }, + { 0x021c, 0x68021c }, + {} + } +}; + +static const struct nvkm_disp_mthd_list +gv100_disp_core_mthd_sor = { + .mthd = 0x0020, + .addr = 0x000020, + .data = { + { 0x0300, 0x680300 }, + { 0x0304, 0x680304 }, + { 0x0308, 0x680308 }, + { 0x030c, 0x68030c }, + {} + } +}; + +static const struct nvkm_disp_mthd_list +gv100_disp_core_mthd_wndw = { + .mthd = 0x0080, + .addr = 0x000080, + .data = { + { 0x1000, 0x681000 }, + { 0x1004, 0x681004 }, + { 0x1008, 0x681008 }, + { 0x100c, 0x68100c }, + { 0x1010, 0x681010 }, + {} + } +}; + +static const struct nvkm_disp_mthd_list +gv100_disp_core_mthd_head = { + .mthd = 0x0400, + .addr = 0x000400, + .data = { + { 0x2000, 0x682000 }, + { 0x2004, 0x682004 }, + { 0x2008, 0x682008 }, + { 0x200c, 0x68200c }, + { 0x2014, 0x682014 }, + { 0x2018, 0x682018 }, + { 0x201c, 0x68201c }, + { 0x2020, 0x682020 }, + { 0x2028, 0x682028 }, + { 0x202c, 0x68202c }, + { 0x2030, 0x682030 }, + { 0x2038, 0x682038 }, + { 0x203c, 0x68203c }, + { 0x2048, 0x682048 }, + { 0x204c, 0x68204c }, + { 0x2050, 0x682050 }, + { 0x2054, 0x682054 }, + { 0x2058, 0x682058 }, + { 0x205c, 0x68205c }, + { 0x2060, 0x682060 }, + { 0x2064, 0x682064 }, + { 0x2068, 0x682068 }, + { 0x206c, 0x68206c }, + { 0x2070, 0x682070 }, + { 0x2074, 0x682074 }, + { 0x2078, 0x682078 }, + { 0x207c, 0x68207c }, + { 0x2080, 0x682080 }, + { 0x2088, 0x682088 }, + { 0x2090, 0x682090 }, + { 0x209c, 0x68209c }, + { 0x20a0, 0x6820a0 }, + { 0x20a4, 0x6820a4 }, + { 0x20a8, 0x6820a8 }, + { 0x20ac, 0x6820ac }, + { 0x2180, 0x682180 }, + { 0x2184, 0x682184 }, + { 0x218c, 0x68218c }, + { 0x2194, 0x682194 }, + { 0x2198, 0x682198 }, + { 0x219c, 0x68219c }, + { 0x21a0, 0x6821a0 }, + { 0x21a4, 0x6821a4 }, + { 0x2214, 0x682214 }, + { 0x2218, 0x682218 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +gv100_disp_core_mthd = { + .name = "Core", + .addr = 0x000000, + .prev = 0x008000, + .data = { + { "Global", 1, &gv100_disp_core_mthd_base }, + { "SOR", 4, &gv100_disp_core_mthd_sor }, + { "WINDOW", 8, &gv100_disp_core_mthd_wndw }, + { "HEAD", 4, &gv100_disp_core_mthd_head }, + {} + } +}; + +static int +gv100_disp_core_idle(struct nvkm_disp_chan *chan) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + nvkm_msec(device, 2000, + u32 stat = nvkm_rd32(device, 0x610630); + if ((stat & 0x001f0000) == 0x000b0000) + return 0; + ); + return -EBUSY; +} + +static u64 +gv100_disp_core_user(struct nvkm_disp_chan *chan, u64 *psize) +{ + *psize = 0x10000; + return 0x680000; +} + +static void +gv100_disp_core_intr(struct nvkm_disp_chan *chan, bool en) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 mask = 0x00000001; + const u32 data = en ? mask : 0; + nvkm_mask(device, 0x611dac, mask, data); +} + +static void +gv100_disp_core_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000000); + gv100_disp_core_idle(chan); + nvkm_mask(device, 0x6104e0, 0x00000002, 0x00000000); + chan->suspend_put = nvkm_rd32(device, 0x680000); +} + +static int +gv100_disp_core_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + + nvkm_wr32(device, 0x610b24, lower_32_bits(chan->push)); + nvkm_wr32(device, 0x610b20, upper_32_bits(chan->push)); + nvkm_wr32(device, 0x610b28, 0x00000001); + nvkm_wr32(device, 0x610b2c, 0x00000040); + + nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000010); + nvkm_wr32(device, 0x680000, chan->suspend_put); + nvkm_wr32(device, 0x6104e0, 0x00000013); + return gv100_disp_core_idle(chan); +} + +static const struct nvkm_disp_chan_func +gv100_disp_core_func = { + .push = nv50_disp_dmac_push, + .init = gv100_disp_core_init, + .fini = gv100_disp_core_fini, + .intr = gv100_disp_core_intr, + .user = gv100_disp_core_user, + .bind = gv100_disp_dmac_bind, +}; + +const struct nvkm_disp_chan_user +gv100_disp_core = { + .func = &gv100_disp_core_func, + .ctrl = 0, + .user = 0, + .mthd = &gv100_disp_core_mthd, +}; + +#define gv100_disp_caps(p) container_of((p), struct gv100_disp_caps, object) + +struct gv100_disp_caps { + struct nvkm_object object; + struct nvkm_disp *disp; +}; + +static int +gv100_disp_caps_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct gv100_disp_caps *caps = gv100_disp_caps(object); + struct nvkm_device *device = caps->disp->engine.subdev.device; + *type = NVKM_OBJECT_MAP_IO; + *addr = 0x640000 + device->func->resource_addr(device, 0); + *size = 0x1000; + return 0; +} + +static const struct nvkm_object_func +gv100_disp_caps = { + .map = gv100_disp_caps_map, +}; + +int +gv100_disp_caps_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = nvkm_udisp(oclass->parent); + struct gv100_disp_caps *caps; + + if (!(caps = kzalloc(sizeof(*caps), GFP_KERNEL))) + return -ENOMEM; + *pobject = &caps->object; + + nvkm_object_ctor(&gv100_disp_caps, oclass, &caps->object); + caps->disp = disp; + return 0; +} + +void +gv100_disp_super(struct work_struct *work) +{ + struct nvkm_disp *disp = container_of(work, struct nvkm_disp, super.work); + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_head *head; + u32 stat, mask[4]; + + mutex_lock(&disp->super.mutex); + stat = nvkm_rd32(device, 0x6107a8); + + nvkm_debug(subdev, "supervisor %d: %08x\n", ffs(disp->super.pending), stat); + list_for_each_entry(head, &disp->heads, head) { + mask[head->id] = nvkm_rd32(device, 0x6107ac + (head->id * 4)); + HEAD_DBG(head, "%08x", mask[head->id]); + } + + if (disp->super.pending & 0x00000001) { + nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); + nv50_disp_super_1(disp); + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_1_0(disp, head); + } + } else + if (disp->super.pending & 0x00000002) { + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_2_0(disp, head); + } + nvkm_outp_route(disp); + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00010000)) + continue; + nv50_disp_super_2_1(disp, head); + } + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_2_2(disp, head); + } + } else + if (disp->super.pending & 0x00000004) { + list_for_each_entry(head, &disp->heads, head) { + if (!(mask[head->id] & 0x00001000)) + continue; + nv50_disp_super_3_0(disp, head); + } + } + + list_for_each_entry(head, &disp->heads, head) + nvkm_wr32(device, 0x6107ac + (head->id * 4), 0x00000000); + + nvkm_wr32(device, 0x6107a8, 0x80000000); + mutex_unlock(&disp->super.mutex); +} + +static void +gv100_disp_exception(struct nvkm_disp *disp, int chid) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x611020 + (chid * 12)); + u32 type = (stat & 0x00007000) >> 12; + u32 mthd = (stat & 0x00000fff) << 2; + const struct nvkm_enum *reason = + nvkm_enum_find(nv50_disp_intr_error_type, type); + + /*TODO: Suspect 33->41 are for WRBK channel exceptions, but we + * don't support those currently. + * + * CORE+WIN CHIDs map directly to the FE_EXCEPT() slots. + */ + if (chid <= 32) { + u32 data = nvkm_rd32(device, 0x611024 + (chid * 12)); + u32 code = nvkm_rd32(device, 0x611028 + (chid * 12)); + nvkm_error(subdev, "chid %d stat %08x reason %d [%s] " + "mthd %04x data %08x code %08x\n", + chid, stat, type, reason ? reason->name : "", + mthd, data, code); + } else { + nvkm_error(subdev, "chid %d stat %08x reason %d [%s] " + "mthd %04x\n", + chid, stat, type, reason ? reason->name : "", mthd); + } + + if (chid < ARRAY_SIZE(disp->chan) && disp->chan[chid]) { + switch (mthd) { + case 0x0200: + nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); + break; + default: + break; + } + } + + nvkm_wr32(device, 0x611020 + (chid * 12), 0x90000000); +} + +static void +gv100_disp_intr_ctrl_disp(struct nvkm_disp *disp) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x611c30); + + if (stat & 0x00000007) { + disp->super.pending = (stat & 0x00000007); + queue_work(disp->super.wq, &disp->super.work); + nvkm_wr32(device, 0x611860, disp->super.pending); + stat &= ~0x00000007; + } + + /*TODO: I would guess this is VBIOS_RELEASE, however, NFI how to + * ACK it, nor does RM appear to bother. + */ + if (stat & 0x00000008) + stat &= ~0x00000008; + + if (stat & 0x00000080) { + u32 error = nvkm_mask(device, 0x611848, 0x00000000, 0x00000000); + nvkm_warn(subdev, "error %08x\n", error); + stat &= ~0x00000080; + } + + if (stat & 0x00000100) { + unsigned long wndws = nvkm_rd32(device, 0x611858); + unsigned long other = nvkm_rd32(device, 0x61185c); + int wndw; + + nvkm_wr32(device, 0x611858, wndws); + nvkm_wr32(device, 0x61185c, other); + + /* AWAKEN_OTHER_CORE. */ + if (other & 0x00000001) + nv50_disp_chan_uevent_send(disp, 0); + + /* AWAKEN_WIN_CH(n). */ + for_each_set_bit(wndw, &wndws, disp->wndw.nr) { + nv50_disp_chan_uevent_send(disp, 1 + wndw); + } + } + + if (stat) + nvkm_warn(subdev, "ctrl %08x\n", stat); +} + +static void +gv100_disp_intr_exc_other(struct nvkm_disp *disp) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x611854); + unsigned long mask; + int head; + + if (stat & 0x00000001) { + nvkm_wr32(device, 0x611854, 0x00000001); + gv100_disp_exception(disp, 0); + stat &= ~0x00000001; + } + + if ((mask = (stat & 0x00ff0000) >> 16)) { + for_each_set_bit(head, &mask, disp->wndw.nr) { + nvkm_wr32(device, 0x611854, 0x00010000 << head); + gv100_disp_exception(disp, 73 + head); + stat &= ~(0x00010000 << head); + } + } + + if (stat) { + nvkm_warn(subdev, "exception %08x\n", stat); + nvkm_wr32(device, 0x611854, stat); + } +} + +static void +gv100_disp_intr_exc_winim(struct nvkm_disp *disp) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + unsigned long stat = nvkm_rd32(device, 0x611850); + int wndw; + + for_each_set_bit(wndw, &stat, disp->wndw.nr) { + nvkm_wr32(device, 0x611850, BIT(wndw)); + gv100_disp_exception(disp, 33 + wndw); + stat &= ~BIT(wndw); + } + + if (stat) { + nvkm_warn(subdev, "wimm %08x\n", (u32)stat); + nvkm_wr32(device, 0x611850, stat); + } +} + +static void +gv100_disp_intr_exc_win(struct nvkm_disp *disp) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + unsigned long stat = nvkm_rd32(device, 0x61184c); + int wndw; + + for_each_set_bit(wndw, &stat, disp->wndw.nr) { + nvkm_wr32(device, 0x61184c, BIT(wndw)); + gv100_disp_exception(disp, 1 + wndw); + stat &= ~BIT(wndw); + } + + if (stat) { + nvkm_warn(subdev, "wndw %08x\n", (u32)stat); + nvkm_wr32(device, 0x61184c, stat); + } +} + +static void +gv100_disp_intr_head_timing(struct nvkm_disp *disp, int head) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x611800 + (head * 0x04)); + + /* LAST_DATA, LOADV. */ + if (stat & 0x00000003) { + nvkm_wr32(device, 0x611800 + (head * 0x04), stat & 0x00000003); + stat &= ~0x00000003; + } + + if (stat & 0x00000004) { + nvkm_disp_vblank(disp, head); + nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000004); + stat &= ~0x00000004; + } + + if (stat) { + nvkm_warn(subdev, "head %08x\n", stat); + nvkm_wr32(device, 0x611800 + (head * 0x04), stat); + } +} + +void +gv100_disp_intr(struct nvkm_disp *disp) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x611ec0); + unsigned long mask; + int head; + + if ((mask = (stat & 0x000000ff))) { + for_each_set_bit(head, &mask, 8) { + gv100_disp_intr_head_timing(disp, head); + stat &= ~BIT(head); + } + } + + if (stat & 0x00000200) { + gv100_disp_intr_exc_win(disp); + stat &= ~0x00000200; + } + + if (stat & 0x00000400) { + gv100_disp_intr_exc_winim(disp); + stat &= ~0x00000400; + } + + if (stat & 0x00000800) { + gv100_disp_intr_exc_other(disp); + stat &= ~0x00000800; + } + + if (stat & 0x00001000) { + gv100_disp_intr_ctrl_disp(disp); + stat &= ~0x00001000; + } + + if (stat) + nvkm_warn(subdev, "intr %08x\n", stat); +} + +void +gv100_disp_fini(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + nvkm_wr32(device, 0x611db0, 0x00000000); +} + +static int +gv100_disp_init(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_head *head; + int i, j; + u32 tmp; + + /* Claim ownership of display. */ + if (nvkm_rd32(device, 0x6254e8) & 0x00000002) { + nvkm_mask(device, 0x6254e8, 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x6254e8) & 0x00000002)) + break; + ) < 0) + return -EBUSY; + } + + /* Lock pin capabilities. */ + tmp = nvkm_rd32(device, 0x610068); + nvkm_wr32(device, 0x640008, tmp); + + /* SOR capabilities. */ + for (i = 0; i < disp->sor.nr; i++) { + tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); + nvkm_mask(device, 0x640000, 0x00000100 << i, 0x00000100 << i); + nvkm_wr32(device, 0x640144 + (i * 0x08), tmp); + } + + /* Head capabilities. */ + list_for_each_entry(head, &disp->heads, head) { + const int id = head->id; + + /* RG. */ + tmp = nvkm_rd32(device, 0x616300 + (id * 0x800)); + nvkm_wr32(device, 0x640048 + (id * 0x020), tmp); + + /* POSTCOMP. */ + for (j = 0; j < 6 * 4; j += 4) { + tmp = nvkm_rd32(device, 0x616100 + (id * 0x800) + j); + nvkm_wr32(device, 0x640030 + (id * 0x20) + j, tmp); + } + } + + /* Window capabilities. */ + for (i = 0; i < disp->wndw.nr; i++) { + nvkm_mask(device, 0x640004, 1 << i, 1 << i); + for (j = 0; j < 6 * 4; j += 4) { + tmp = nvkm_rd32(device, 0x630050 + (i * 0x800) + j); + nvkm_wr32(device, 0x6401e4 + (i * 0x20) + j, tmp); + } + } + + /* IHUB capabilities. */ + for (i = 0; i < 4; i++) { + tmp = nvkm_rd32(device, 0x62e000 + (i * 0x04)); + nvkm_wr32(device, 0x640010 + (i * 0x04), tmp); + } + + nvkm_mask(device, 0x610078, 0x00000001, 0x00000001); + + /* Setup instance memory. */ + switch (nvkm_memory_target(disp->inst->memory)) { + case NVKM_MEM_TARGET_VRAM: tmp = 0x00000001; break; + case NVKM_MEM_TARGET_NCOH: tmp = 0x00000002; break; + case NVKM_MEM_TARGET_HOST: tmp = 0x00000003; break; + default: + break; + } + nvkm_wr32(device, 0x610010, 0x00000008 | tmp); + nvkm_wr32(device, 0x610014, disp->inst->addr >> 16); + + /* CTRL_DISP: AWAKEN, ERROR, SUPERVISOR[1-3]. */ + nvkm_wr32(device, 0x611cf0, 0x00000187); /* MSK. */ + nvkm_wr32(device, 0x611db0, 0x00000187); /* EN. */ + + /* EXC_OTHER: CURSn, CORE. */ + nvkm_wr32(device, 0x611cec, disp->head.mask << 16 | + 0x00000001); /* MSK. */ + nvkm_wr32(device, 0x611dac, 0x00000000); /* EN. */ + + /* EXC_WINIM. */ + nvkm_wr32(device, 0x611ce8, disp->wndw.mask); /* MSK. */ + nvkm_wr32(device, 0x611da8, 0x00000000); /* EN. */ + + /* EXC_WIN. */ + nvkm_wr32(device, 0x611ce4, disp->wndw.mask); /* MSK. */ + nvkm_wr32(device, 0x611da4, 0x00000000); /* EN. */ + + /* HEAD_TIMING(n): VBLANK. */ + list_for_each_entry(head, &disp->heads, head) { + const u32 hoff = head->id * 4; + nvkm_wr32(device, 0x611cc0 + hoff, 0x00000004); /* MSK. */ + nvkm_wr32(device, 0x611d80 + hoff, 0x00000000); /* EN. */ + } + + /* OR. */ + nvkm_wr32(device, 0x611cf4, 0x00000000); /* MSK. */ + nvkm_wr32(device, 0x611db4, 0x00000000); /* EN. */ + return 0; +} + +static const struct nvkm_disp_func +gv100_disp = { + .oneinit = nv50_disp_oneinit, + .init = gv100_disp_init, + .fini = gv100_disp_fini, + .intr = gv100_disp_intr, + .super = gv100_disp_super, + .uevent = &gv100_disp_chan_uevent, + .wndw = { .cnt = gv100_disp_wndw_cnt }, + .head = { .cnt = gv100_head_cnt, .new = gv100_head_new }, + .sor = { .cnt = gv100_sor_cnt, .new = gv100_sor_new }, + .ramht_size = 0x2000, + .root = { 0, 0,GV100_DISP }, + .user = { + {{-1,-1,GV100_DISP_CAPS }, gv100_disp_caps_new }, + {{ 0, 0,GV100_DISP_CURSOR }, nvkm_disp_chan_new, &gv100_disp_curs }, + {{ 0, 0,GV100_DISP_WINDOW_IMM_CHANNEL_DMA}, nvkm_disp_wndw_new, &gv100_disp_wimm }, + {{ 0, 0,GV100_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gv100_disp_core }, + {{ 0, 0,GV100_DISP_WINDOW_CHANNEL_DMA }, nvkm_disp_wndw_new, &gv100_disp_wndw }, + {} + }, +}; + +int +gv100_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&gv100_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c new file mode 100644 index 000000000..1ccfc8314 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +#include "hdmi.h" + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len) +{ + u32 header = 0; + u32 subpack0_low = 0; + u32 subpack0_high = 0; + u32 subpack1_low = 0; + u32 subpack1_high = 0; + + switch (len) { + /* + * "When in doubt, use brute force." + * -- Ken Thompson. + */ + default: + /* + * We presume that no valid frame is longer than 17 + * octets, including header... And truncate to that + * if it's longer. + */ + case 17: + subpack1_high = (raw_frame[16] << 16); + fallthrough; + case 16: + subpack1_high |= (raw_frame[15] << 8); + fallthrough; + case 15: + subpack1_high |= raw_frame[14]; + fallthrough; + case 14: + subpack1_low = (raw_frame[13] << 24); + fallthrough; + case 13: + subpack1_low |= (raw_frame[12] << 16); + fallthrough; + case 12: + subpack1_low |= (raw_frame[11] << 8); + fallthrough; + case 11: + subpack1_low |= raw_frame[10]; + fallthrough; + case 10: + subpack0_high = (raw_frame[9] << 16); + fallthrough; + case 9: + subpack0_high |= (raw_frame[8] << 8); + fallthrough; + case 8: + subpack0_high |= raw_frame[7]; + fallthrough; + case 7: + subpack0_low = (raw_frame[6] << 24); + fallthrough; + case 6: + subpack0_low |= (raw_frame[5] << 16); + fallthrough; + case 5: + subpack0_low |= (raw_frame[4] << 8); + fallthrough; + case 4: + subpack0_low |= raw_frame[3]; + fallthrough; + case 3: + header = (raw_frame[2] << 16); + fallthrough; + case 2: + header |= (raw_frame[1] << 8); + fallthrough; + case 1: + header |= raw_frame[0]; + fallthrough; + case 0: + break; + } + + packed_frame->header = header; + packed_frame->subpack0_low = subpack0_low; + packed_frame->subpack0_high = subpack0_high; + packed_frame->subpack1_low = subpack1_low; + packed_frame->subpack1_high = subpack1_high; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h new file mode 100644 index 000000000..fb1c3e3c5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_HDMI_H__ +#define __NVKM_DISP_HDMI_H__ +#include "ior.h" + +struct packed_hdmi_infoframe { + u32 header; + u32 subpack0_low; + u32 subpack0_high; + u32 subpack1_low; + u32 subpack1_high; +}; + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c new file mode 100644 index 000000000..83152c26f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c @@ -0,0 +1,105 @@ +/* + * Copyright 2017 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 "head.h" + +#include + +#include +#include + +struct nvkm_head * +nvkm_head_find(struct nvkm_disp *disp, int id) +{ + struct nvkm_head *head; + list_for_each_entry(head, &disp->heads, head) { + if (head->id == id) + return head; + } + return NULL; +} + +int +nvkm_head_mthd_scanoutpos(struct nvkm_object *object, + struct nvkm_head *head, void *data, u32 size) +{ + union { + struct nv04_disp_scanoutpos_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "head scanoutpos size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "head scanoutpos vers %d\n", + args->v0.version); + + head->func->state(head, &head->arm); + args->v0.vtotal = head->arm.vtotal; + args->v0.vblanks = head->arm.vblanks; + args->v0.vblanke = head->arm.vblanke; + args->v0.htotal = head->arm.htotal; + args->v0.hblanks = head->arm.hblanks; + args->v0.hblanke = head->arm.hblanke; + + /* We don't support reading htotal/vtotal on pre-NV50 VGA, + * so we have to give up and trigger the timestamping + * fallback in the drm core. + */ + if (!args->v0.vtotal || !args->v0.htotal) + return -ENOTSUPP; + + args->v0.time[0] = ktime_to_ns(ktime_get()); + head->func->rgpos(head, &args->v0.hline, &args->v0.vline); + args->v0.time[1] = ktime_to_ns(ktime_get()); + } else + return ret; + + return 0; +} + +void +nvkm_head_del(struct nvkm_head **phead) +{ + struct nvkm_head *head = *phead; + if (head) { + HEAD_DBG(head, "dtor"); + list_del(&head->head); + kfree(*phead); + *phead = NULL; + } +} + +int +nvkm_head_new_(const struct nvkm_head_func *func, + struct nvkm_disp *disp, int id) +{ + struct nvkm_head *head; + if (!(head = kzalloc(sizeof(*head), GFP_KERNEL))) + return -ENOMEM; + head->func = func; + head->disp = disp; + head->id = id; + list_add_tail(&head->head, &disp->heads); + HEAD_DBG(head, "ctor"); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h new file mode 100644 index 000000000..84a298919 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_HEAD_H__ +#define __NVKM_DISP_HEAD_H__ +#include "priv.h" + +struct nvkm_head { + const struct nvkm_head_func *func; + struct nvkm_disp *disp; + int id; + + struct list_head head; + + struct nvkm_head_state { + u16 htotal; + u16 hsynce; + u16 hblanke; + u16 hblanks; + u16 vtotal; + u16 vsynce; + u16 vblanke; + u16 vblanks; + u32 hz; + + /* Prior to GF119, these are set by the OR. */ + struct { + u8 depth; + } or; + } arm, asy; +}; + +int nvkm_head_new_(const struct nvkm_head_func *, struct nvkm_disp *, int id); +void nvkm_head_del(struct nvkm_head **); +int nvkm_head_mthd_scanoutpos(struct nvkm_object *, + struct nvkm_head *, void *, u32); +struct nvkm_head *nvkm_head_find(struct nvkm_disp *, int id); + +struct nvkm_head_func { + void (*state)(struct nvkm_head *, struct nvkm_head_state *); + void (*rgpos)(struct nvkm_head *, u16 *hline, u16 *vline); + void (*rgclk)(struct nvkm_head *, int div); + void (*vblank_get)(struct nvkm_head *); + void (*vblank_put)(struct nvkm_head *); +}; + +int nv50_head_cnt(struct nvkm_disp *, unsigned long *); +int nv50_head_new(struct nvkm_disp *, int id); +void nv50_head_rgpos(struct nvkm_head *, u16 *, u16 *); + +int gf119_head_cnt(struct nvkm_disp *, unsigned long *); +int gf119_head_new(struct nvkm_disp *, int id); +void gf119_head_rgclk(struct nvkm_head *, int); + +int gv100_head_cnt(struct nvkm_disp *, unsigned long *); +int gv100_head_new(struct nvkm_disp *, int id); + +#define HEAD_MSG(h,l,f,a...) do { \ + struct nvkm_head *_h = (h); \ + nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \ +} while(0) +#define HEAD_WARN(h,f,a...) HEAD_MSG((h), warn, f, ##a) +#define HEAD_DBG(h,f,a...) HEAD_MSG((h), debug, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c new file mode 100644 index 000000000..e420bf2e4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c @@ -0,0 +1,72 @@ +/* + * Copyright 2017 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 "ior.h" + +static const char * +nvkm_ior_name[] = { + [DAC] = "DAC", + [SOR] = "SOR", + [PIOR] = "PIOR", +}; + +struct nvkm_ior * +nvkm_ior_find(struct nvkm_disp *disp, enum nvkm_ior_type type, int id) +{ + struct nvkm_ior *ior; + list_for_each_entry(ior, &disp->iors, head) { + if (ior->type == type && (id < 0 || ior->id == id)) + return ior; + } + return NULL; +} + +void +nvkm_ior_del(struct nvkm_ior **pior) +{ + struct nvkm_ior *ior = *pior; + if (ior) { + IOR_DBG(ior, "dtor"); + list_del(&ior->head); + kfree(*pior); + *pior = NULL; + } +} + +int +nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, + enum nvkm_ior_type type, int id, bool hda) +{ + struct nvkm_ior *ior; + if (!(ior = kzalloc(sizeof(*ior), GFP_KERNEL))) + return -ENOMEM; + ior->func = func; + ior->disp = disp; + ior->type = type; + ior->id = id; + ior->hda = hda; + snprintf(ior->name, sizeof(ior->name), "%s-%d", nvkm_ior_name[ior->type], ior->id); + list_add_tail(&ior->head, &disp->iors); + IOR_DBG(ior, "ctor"); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h new file mode 100644 index 000000000..671c4674f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_IOR_H__ +#define __NVKM_DISP_IOR_H__ +#include "priv.h" +struct nvkm_i2c_aux; + +struct nvkm_ior { + const struct nvkm_ior_func *func; + struct nvkm_disp *disp; + enum nvkm_ior_type { + DAC, + SOR, + PIOR, + } type; + int id; + bool hda; + char name[8]; + + struct list_head head; + bool identity; + + struct nvkm_ior_state { + struct nvkm_outp *outp; + unsigned rgdiv; + unsigned proto_evo:4; + enum nvkm_ior_proto { + CRT, + TV, + TMDS, + LVDS, + DP, + UNKNOWN + } proto:3; + unsigned link:2; + unsigned head:8; + } arm, asy; + + /* Armed DP state. */ + struct { + bool mst; + bool ef; + u8 nr; + u8 bw; + } dp; + + /* Armed TMDS state. */ + struct { + bool high_speed; + } tmds; +}; + +struct nvkm_ior_func { + struct { + int (*get)(struct nvkm_outp *, int *link); + void (*set)(struct nvkm_outp *, struct nvkm_ior *); + } route; + + void (*state)(struct nvkm_ior *, struct nvkm_ior_state *); + void (*power)(struct nvkm_ior *, bool normal, bool pu, + bool data, bool vsync, bool hsync); + int (*sense)(struct nvkm_ior *, u32 loadval); + void (*clock)(struct nvkm_ior *); + void (*war_2)(struct nvkm_ior *); + void (*war_3)(struct nvkm_ior *); + + struct { + void (*ctrl)(struct nvkm_ior *, int head, bool enable, + u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size, + u8 *vendor, u8 vendor_size); + void (*scdc)(struct nvkm_ior *, u8 scdc); + } hdmi; + + const struct nvkm_ior_func_dp { + u8 lanes[4]; + int (*links)(struct nvkm_ior *, struct nvkm_i2c_aux *); + void (*power)(struct nvkm_ior *, int nr); + void (*pattern)(struct nvkm_ior *, int pattern); + void (*drive)(struct nvkm_ior *, int ln, int pc, + int dc, int pe, int tx_pu); + void (*vcpi)(struct nvkm_ior *, int head, u8 slot, + u8 slot_nr, u16 pbn, u16 aligned); + void (*audio)(struct nvkm_ior *, int head, bool enable); + void (*audio_sym)(struct nvkm_ior *, int head, u16 h, u32 v); + void (*activesym)(struct nvkm_ior *, int head, + u8 TU, u8 VTUa, u8 VTUf, u8 VTUi); + void (*watermark)(struct nvkm_ior *, int head, u8 watermark); + } *dp; + + const struct nvkm_ior_func_hda { + void (*hpd)(struct nvkm_ior *, int head, bool present); + void (*eld)(struct nvkm_ior *, int head, u8 *data, u8 size); + void (*device_entry)(struct nvkm_ior *, int head); + } *hda; +}; + +int nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *, + enum nvkm_ior_type type, int id, bool hda); +void nvkm_ior_del(struct nvkm_ior **); +struct nvkm_ior *nvkm_ior_find(struct nvkm_disp *, enum nvkm_ior_type, int id); + +static inline u32 +nv50_ior_base(struct nvkm_ior *ior) +{ + return ior->id * 0x800; +} + +int nv50_dac_cnt(struct nvkm_disp *, unsigned long *); +int nv50_dac_new(struct nvkm_disp *, int); +void nv50_dac_power(struct nvkm_ior *, bool, bool, bool, bool, bool); +int nv50_dac_sense(struct nvkm_ior *, u32); + +int gf119_dac_cnt(struct nvkm_disp *, unsigned long *); +int gf119_dac_new(struct nvkm_disp *, int); + +static inline u32 +nv50_sor_link(struct nvkm_ior *ior) +{ + return nv50_ior_base(ior) + ((ior->asy.link == 2) * 0x80); +} + +int nv50_sor_cnt(struct nvkm_disp *, unsigned long *); +void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); +void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool); +void nv50_sor_clock(struct nvkm_ior *); + +int g84_sor_new(struct nvkm_disp *, int); +void g84_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); + +int g94_sor_cnt(struct nvkm_disp *, unsigned long *); +void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); +extern const struct nvkm_ior_func_dp g94_sor_dp; +int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *); +void g94_sor_dp_power(struct nvkm_ior *, int); +void g94_sor_dp_pattern(struct nvkm_ior *, int); +void g94_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); +void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); +void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8); +void g94_sor_dp_watermark(struct nvkm_ior *, int, u8); + +void gt215_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); +void gt215_sor_dp_audio(struct nvkm_ior *, int, bool); +extern const struct nvkm_ior_func_hda gt215_sor_hda; + +int gf119_sor_cnt(struct nvkm_disp *, unsigned long *); +void gf119_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); +void gf119_sor_clock(struct nvkm_ior *); +extern const struct nvkm_ior_func_dp gf119_sor_dp; +int gf119_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *); +void gf119_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); +void gf119_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16); +void gf119_sor_dp_audio(struct nvkm_ior *, int, bool); +void gf119_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); +void gf119_sor_dp_watermark(struct nvkm_ior *, int, u8); +extern const struct nvkm_ior_func_hda gf119_sor_hda; +void gf119_sor_hda_hpd(struct nvkm_ior *, int, bool); +void gf119_sor_hda_eld(struct nvkm_ior *, int, u8 *, u8); + +int gk104_sor_new(struct nvkm_disp *, int); +void gk104_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); + +void gm107_sor_dp_pattern(struct nvkm_ior *, int); + +void gm200_sor_route_set(struct nvkm_outp *, struct nvkm_ior *); +int gm200_sor_route_get(struct nvkm_outp *, int *); +void gm200_sor_hdmi_scdc(struct nvkm_ior *, u8); +extern const struct nvkm_ior_func_dp gm200_sor_dp; +void gm200_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); + +int gp100_sor_new(struct nvkm_disp *, int); + +int gv100_sor_cnt(struct nvkm_disp *, unsigned long *); +void gv100_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); +void gv100_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); +void gv100_sor_dp_audio(struct nvkm_ior *, int, bool); +void gv100_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); +void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8); +extern const struct nvkm_ior_func_hda gv100_sor_hda; + +void tu102_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16); + +int nv50_pior_cnt(struct nvkm_disp *, unsigned long *); +int nv50_pior_new(struct nvkm_disp *, int); +void nv50_pior_depth(struct nvkm_ior *, struct nvkm_ior_state *, u32 ctrl); + +#define IOR_MSG(i,l,f,a...) do { \ + struct nvkm_ior *_ior = (i); \ + nvkm_##l(&_ior->disp->engine.subdev, "%s: "f"\n", _ior->name, ##a); \ +} while(0) +#define IOR_WARN(i,f,a...) IOR_MSG((i), warn, f, ##a) +#define IOR_DBG(i,f,a...) IOR_MSG((i), debug, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c new file mode 100644 index 000000000..916b1d477 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c @@ -0,0 +1,74 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +static const struct nvkm_ior_func +mcp77_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = g84_sor_hdmi_ctrl, + }, + .dp = &g94_sor_dp, +}; + +static int +mcp77_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&mcp77_sor, disp, SOR, id, false); +} + +static const struct nvkm_disp_func +mcp77_disp = { + .oneinit = nv50_disp_oneinit, + .init = nv50_disp_init, + .fini = nv50_disp_fini, + .intr = nv50_disp_intr, + .super = nv50_disp_super, + .uevent = &nv50_disp_chan_uevent, + .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, + .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, + .sor = { .cnt = g94_sor_cnt, .new = mcp77_sor_new }, + .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, + .root = { 0,0,GT206_DISP }, + .user = { + {{0,0, G82_DISP_CURSOR }, nvkm_disp_chan_new, & nv50_disp_curs }, + {{0,0, G82_DISP_OVERLAY }, nvkm_disp_chan_new, & nv50_disp_oimm }, + {{0,0,GT200_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, & g84_disp_base }, + {{0,0,GT206_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, & g94_disp_core }, + {{0,0,GT200_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, >200_disp_ovly }, + {} + }, +}; + +int +mcp77_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&mcp77_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c new file mode 100644 index 000000000..a5a0b9439 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c @@ -0,0 +1,88 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" + +#include + +static const struct nvkm_ior_func_dp +mcp89_sor_dp = { + .lanes = { 3, 2, 1, 0 }, + .links = g94_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = g94_sor_dp_pattern, + .drive = g94_sor_dp_drive, + .audio = gt215_sor_dp_audio, + .audio_sym = g94_sor_dp_audio_sym, + .activesym = g94_sor_dp_activesym, + .watermark = g94_sor_dp_watermark, +}; + +static const struct nvkm_ior_func +mcp89_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = gt215_sor_hdmi_ctrl, + }, + .dp = &mcp89_sor_dp, + .hda = >215_sor_hda, +}; + +static int +mcp89_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&mcp89_sor, disp, SOR, id, true); +} + +static const struct nvkm_disp_func +mcp89_disp = { + .oneinit = nv50_disp_oneinit, + .init = nv50_disp_init, + .fini = nv50_disp_fini, + .intr = nv50_disp_intr, + .super = nv50_disp_super, + .uevent = &nv50_disp_chan_uevent, + .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, + .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, + .sor = { .cnt = g94_sor_cnt, .new = mcp89_sor_new }, + .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, + .root = { 0,0,GT214_DISP }, + .user = { + {{0,0,GT214_DISP_CURSOR }, nvkm_disp_chan_new, &nv50_disp_curs }, + {{0,0,GT214_DISP_OVERLAY }, nvkm_disp_chan_new, &nv50_disp_oimm }, + {{0,0,GT214_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, & g84_disp_base }, + {{0,0,GT214_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, & g94_disp_core }, + {{0,0,GT214_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, & g84_disp_ovly }, + {} + }, +}; + +int +mcp89_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&mcp89_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c new file mode 100644 index 000000000..e4cf11a33 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c @@ -0,0 +1,130 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "head.h" + +#include + +static void +nv04_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000000); +} + +static void +nv04_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000001); +} + +static void +nv04_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + u32 data = nvkm_rd32(device, 0x600868 + (head->id * 0x2000)); + *hline = (data & 0xffff0000) >> 16; + *vline = (data & 0x0000ffff); +} + +static void +nv04_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x0200; + state->vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0x0000ffff; + state->vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0x0000ffff; + state->vblanke = state->vtotal - 1; + state->hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0x0000ffff; + state->htotal = nvkm_rd32(device, 0x680824 + hoff) & 0x0000ffff; + state->hblanke = state->htotal - 1; +} + +static const struct nvkm_head_func +nv04_head = { + .state = nv04_head_state, + .rgpos = nv04_head_rgpos, + .vblank_get = nv04_head_vblank_get, + .vblank_put = nv04_head_vblank_put, +}; + +static int +nv04_head_new(struct nvkm_disp *disp, int id) +{ + return nvkm_head_new_(&nv04_head, disp, id); +} + +static void +nv04_disp_intr(struct nvkm_disp *disp) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 crtc0 = nvkm_rd32(device, 0x600100); + u32 crtc1 = nvkm_rd32(device, 0x602100); + u32 pvideo; + + if (crtc0 & 0x00000001) { + nvkm_disp_vblank(disp, 0); + nvkm_wr32(device, 0x600100, 0x00000001); + } + + if (crtc1 & 0x00000001) { + nvkm_disp_vblank(disp, 1); + nvkm_wr32(device, 0x602100, 0x00000001); + } + + if (device->chipset >= 0x10 && device->chipset <= 0x40) { + pvideo = nvkm_rd32(device, 0x8100); + if (pvideo & ~0x11) + nvkm_info(subdev, "PVIDEO intr: %08x\n", pvideo); + nvkm_wr32(device, 0x8100, pvideo); + } +} + +static const struct nvkm_disp_func +nv04_disp = { + .intr = nv04_disp_intr, + .root = { 0, 0, NV04_DISP }, + .user = { {} }, +}; + +int +nv04_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + int ret, i; + + ret = nvkm_disp_new_(&nv04_disp, device, type, inst, pdisp); + if (ret) + return ret; + + for (i = 0; i < 2; i++) { + ret = nv04_head_new(*pdisp, i); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c new file mode 100644 index 000000000..a46e13cc9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -0,0 +1,1643 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "head.h" +#include "ior.h" +#include "outp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static void +nv50_pior_clock(struct nvkm_ior *pior) +{ + struct nvkm_device *device = pior->disp->engine.subdev.device; + const u32 poff = nv50_ior_base(pior); + + nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001); +} + +static int +nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux) +{ + int ret = nvkm_i2c_aux_lnk_ctl(aux, pior->dp.nr, pior->dp.bw, pior->dp.ef); + if (ret) + return ret; + + return 1; +} + +static const struct nvkm_ior_func_dp +nv50_pior_dp = { + .links = nv50_pior_dp_links, +}; + +static void +nv50_pior_power_wait(struct nvkm_device *device, u32 poff) +{ + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61e004 + poff) & 0x80000000)) + break; + ); +} + +static void +nv50_pior_power(struct nvkm_ior *pior, bool normal, bool pu, bool data, bool vsync, bool hsync) +{ + struct nvkm_device *device = pior->disp->engine.subdev.device; + const u32 poff = nv50_ior_base(pior); + const u32 shift = normal ? 0 : 16; + const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift; + const u32 field = 0x80000000 | (0x00000101 << shift); + + nv50_pior_power_wait(device, poff); + nvkm_mask(device, 0x61e004 + poff, field, state); + nv50_pior_power_wait(device, poff); +} + +void +nv50_pior_depth(struct nvkm_ior *ior, struct nvkm_ior_state *state, u32 ctrl) +{ + /* GF119 moves this information to per-head methods, which is + * a lot more convenient, and where our shared code expect it. + */ + if (state->head && state == &ior->asy) { + struct nvkm_head *head = nvkm_head_find(ior->disp, __ffs(state->head)); + + if (!WARN_ON(!head)) { + struct nvkm_head_state *state = &head->asy; + switch ((ctrl & 0x000f0000) >> 16) { + case 6: state->or.depth = 30; break; + case 5: state->or.depth = 24; break; + case 2: state->or.depth = 18; break; + case 0: state->or.depth = 18; break; /*XXX*/ + default: + state->or.depth = 18; + WARN_ON(1); + break; + } + } + } +} + +static void +nv50_pior_state(struct nvkm_ior *pior, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = pior->disp->engine.subdev.device; + const u32 coff = pior->id * 8 + (state == &pior->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610b80 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + state->rgdiv = 1; + switch (state->proto_evo) { + case 0: state->proto = TMDS; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x00000003; + nv50_pior_depth(pior, state, ctrl); +} + +static const struct nvkm_ior_func +nv50_pior = { + .state = nv50_pior_state, + .power = nv50_pior_power, + .clock = nv50_pior_clock, + .dp = &nv50_pior_dp, +}; + +int +nv50_pior_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&nv50_pior, disp, PIOR, id, false); +} + +int +nv50_pior_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + *pmask = (nvkm_rd32(device, 0x610184) & 0x70000000) >> 28; + return 3; +} + +void +nv50_sor_clock(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const int div = sor->asy.link == 3; + const u32 soff = nv50_ior_base(sor); + + nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div); +} + +static void +nv50_sor_power_wait(struct nvkm_device *device, u32 soff) +{ + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000)) + break; + ); +} + +void +nv50_sor_power(struct nvkm_ior *sor, bool normal, bool pu, bool data, bool vsync, bool hsync) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 shift = normal ? 0 : 16; + const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift; + const u32 field = 0x80000000 | (0x00000001 << shift); + + nv50_sor_power_wait(device, soff); + nvkm_mask(device, 0x61c004 + soff, field, state); + nv50_sor_power_wait(device, soff); + + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) + break; + ); +} + +void +nv50_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 coff = sor->id * 8 + (state == &sor->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610b70 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = LVDS; state->link = 1; break; + case 1: state->proto = TMDS; state->link = 1; break; + case 2: state->proto = TMDS; state->link = 2; break; + case 5: state->proto = TMDS; state->link = 3; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x00000003; +} + +static const struct nvkm_ior_func +nv50_sor = { + .state = nv50_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, +}; + +static int +nv50_sor_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&nv50_sor, disp, SOR, id, false); +} + +int +nv50_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + *pmask = (nvkm_rd32(device, 0x610184) & 0x03000000) >> 24; + return 2; +} + +static void +nv50_dac_clock(struct nvkm_ior *dac) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); + + nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000); +} + +int +nv50_dac_sense(struct nvkm_ior *dac, u32 loadval) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); + + dac->func->power(dac, false, true, false, false, false); + + nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval); + mdelay(9); + udelay(500); + loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000); + + dac->func->power(dac, false, false, false, false, false); + if (!(loadval & 0x80000000)) + return -ETIMEDOUT; + + return (loadval & 0x38000000) >> 27; +} + +static void +nv50_dac_power_wait(struct nvkm_device *device, const u32 doff) +{ + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) + break; + ); +} + +void +nv50_dac_power(struct nvkm_ior *dac, bool normal, bool pu, bool data, bool vsync, bool hsync) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); + const u32 shift = normal ? 0 : 16; + const u32 state = 0x80000000 | (0x00000040 * ! pu | + 0x00000010 * ! data | + 0x00000004 * ! vsync | + 0x00000001 * ! hsync) << shift; + const u32 field = 0xc0000000 | (0x00000055 << shift); + + nv50_dac_power_wait(device, doff); + nvkm_mask(device, 0x61a004 + doff, field, state); + nv50_dac_power_wait(device, doff); +} + +static void +nv50_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 coff = dac->id * 8 + (state == &dac->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610b58 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = CRT; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x00000003; +} + +static const struct nvkm_ior_func +nv50_dac = { + .state = nv50_dac_state, + .power = nv50_dac_power, + .sense = nv50_dac_sense, + .clock = nv50_dac_clock, +}; + +int +nv50_dac_new(struct nvkm_disp *disp, int id) +{ + return nvkm_ior_new_(&nv50_dac, disp, DAC, id, false); +} + +int +nv50_dac_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + struct nvkm_device *device = disp->engine.subdev.device; + + *pmask = (nvkm_rd32(device, 0x610184) & 0x00700000) >> 20; + return 3; +} + +static void +nv50_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_mask(device, 0x61002c, (4 << head->id), 0); +} + +static void +nv50_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id)); +} + +static void +nv50_head_rgclk(struct nvkm_head *head, int div) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div); +} + +void +nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x800; + + /* vline read locks hline. */ + *vline = nvkm_rd32(device, 0x616340 + hoff) & 0x0000ffff; + *hline = nvkm_rd32(device, 0x616344 + hoff) & 0x0000ffff; +} + +static void +nv50_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x540 + (state == &head->arm) * 4; + u32 data; + + data = nvkm_rd32(device, 0x610ae8 + hoff); + state->vblanke = (data & 0xffff0000) >> 16; + state->hblanke = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x610af0 + hoff); + state->vblanks = (data & 0xffff0000) >> 16; + state->hblanks = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x610af8 + hoff); + state->vtotal = (data & 0xffff0000) >> 16; + state->htotal = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x610b00 + hoff); + state->vsynce = (data & 0xffff0000) >> 16; + state->hsynce = (data & 0x0000ffff); + state->hz = (nvkm_rd32(device, 0x610ad0 + hoff) & 0x003fffff) * 1000; +} + +static const struct nvkm_head_func +nv50_head = { + .state = nv50_head_state, + .rgpos = nv50_head_rgpos, + .rgclk = nv50_head_rgclk, + .vblank_get = nv50_head_vblank_get, + .vblank_put = nv50_head_vblank_put, +}; + +int +nv50_head_new(struct nvkm_disp *disp, int id) +{ + return nvkm_head_new_(&nv50_head, disp, id); +} + +int +nv50_head_cnt(struct nvkm_disp *disp, unsigned long *pmask) +{ + *pmask = 3; + return 2; +} + + +static void +nv50_disp_mthd_list(struct nvkm_disp *disp, int debug, u32 base, int c, + const struct nvkm_disp_mthd_list *list, int inst) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int i; + + for (i = 0; list->data[i].mthd; i++) { + if (list->data[i].addr) { + u32 next = nvkm_rd32(device, list->data[i].addr + base + 0); + u32 prev = nvkm_rd32(device, list->data[i].addr + base + c); + u32 mthd = list->data[i].mthd + (list->mthd * inst); + const char *name = list->data[i].name; + char mods[16]; + + if (prev != next) + snprintf(mods, sizeof(mods), "-> %08x", next); + else + snprintf(mods, sizeof(mods), "%13c", ' '); + + nvkm_printk_(subdev, debug, info, + "\t%04x: %08x %s%s%s\n", + mthd, prev, mods, name ? " // " : "", + name ? name : ""); + } + } +} + +void +nv50_disp_chan_mthd(struct nvkm_disp_chan *chan, int debug) +{ + struct nvkm_disp *disp = chan->disp; + struct nvkm_subdev *subdev = &disp->engine.subdev; + const struct nvkm_disp_chan_mthd *mthd = chan->mthd; + const struct nvkm_disp_mthd_list *list; + int i, j; + + if (debug > subdev->debug) + return; + if (!mthd) + return; + + for (i = 0; (list = mthd->data[i].mthd) != NULL; i++) { + u32 base = chan->head * mthd->addr; + for (j = 0; j < mthd->data[i].nr; j++, base += list->addr) { + const char *cname = mthd->name; + const char *sname = ""; + char cname_[16], sname_[16]; + + if (mthd->addr) { + snprintf(cname_, sizeof(cname_), "%s %d", + mthd->name, chan->chid.user); + cname = cname_; + } + + if (mthd->data[i].nr > 1) { + snprintf(sname_, sizeof(sname_), " - %s %d", + mthd->data[i].name, j); + sname = sname_; + } + + nvkm_printk_(subdev, debug, info, "%s%s:\n", cname, sname); + nv50_disp_mthd_list(disp, debug, base, mthd->prev, + list, j); + } + } +} + +static void +nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent); + struct nvkm_device *device = disp->engine.subdev.device; + nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000000 << index); + nvkm_wr32(device, 0x610020, 0x00000001 << index); +} + +static void +nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) +{ + struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent); + struct nvkm_device *device = disp->engine.subdev.device; + nvkm_wr32(device, 0x610020, 0x00000001 << index); + nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000001 << index); +} + +void +nv50_disp_chan_uevent_send(struct nvkm_disp *disp, int chid) +{ + nvkm_event_send(&disp->uevent, NVKM_DISP_EVENT_CHAN_AWAKEN, chid, NULL, 0); +} + +const struct nvkm_event_func +nv50_disp_chan_uevent = { + .init = nv50_disp_chan_uevent_init, + .fini = nv50_disp_chan_uevent_fini, +}; + +u64 +nv50_disp_chan_user(struct nvkm_disp_chan *chan, u64 *psize) +{ + *psize = 0x1000; + return 0x640000 + (chan->chid.user * 0x1000); +} + +void +nv50_disp_chan_intr(struct nvkm_disp_chan *chan, bool en) +{ + struct nvkm_device *device = chan->disp->engine.subdev.device; + const u32 mask = 0x00010001 << chan->chid.user; + const u32 data = en ? 0x00010000 << chan->chid.user : 0x00000000; + nvkm_mask(device, 0x610028, mask, data); +} + +static void +nv50_disp_pioc_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_disp *disp = chan->disp; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + nvkm_mask(device, 0x610200 + (ctrl * 0x10), 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x00030000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d timeout: %08x\n", user, + nvkm_rd32(device, 0x610200 + (ctrl * 0x10))); + } +} + +static int +nv50_disp_pioc_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_disp *disp = chan->disp; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + nvkm_wr32(device, 0x610200 + (ctrl * 0x10), 0x00002000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x00030000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d timeout0: %08x\n", user, + nvkm_rd32(device, 0x610200 + (ctrl * 0x10))); + return -EBUSY; + } + + nvkm_wr32(device, 0x610200 + (ctrl * 0x10), 0x00000001); + if (nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x610200 + (ctrl * 0x10)); + if ((tmp & 0x00030000) == 0x00010000) + break; + ) < 0) { + nvkm_error(subdev, "ch %d timeout1: %08x\n", user, + nvkm_rd32(device, 0x610200 + (ctrl * 0x10))); + return -EBUSY; + } + + return 0; +} + +const struct nvkm_disp_chan_func +nv50_disp_pioc_func = { + .init = nv50_disp_pioc_init, + .fini = nv50_disp_pioc_fini, + .intr = nv50_disp_chan_intr, + .user = nv50_disp_chan_user, +}; + +int +nv50_disp_dmac_bind(struct nvkm_disp_chan *chan, struct nvkm_object *object, u32 handle) +{ + return nvkm_ramht_insert(chan->disp->ramht, object, chan->chid.user, -10, handle, + chan->chid.user << 28 | chan->chid.user); +} + +static void +nv50_disp_dmac_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + /* deactivate channel */ + nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00001010, 0x00001000); + nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000003, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x001e0000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d fini timeout, %08x\n", user, + nvkm_rd32(device, 0x610200 + (ctrl * 0x10))); + } + + chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000)); +} + +static int +nv50_disp_dmac_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ctrl = chan->chid.ctrl; + int user = chan->chid.user; + + /* initialise channel for dma command submission */ + nvkm_wr32(device, 0x610204 + (ctrl * 0x0010), chan->push); + nvkm_wr32(device, 0x610208 + (ctrl * 0x0010), 0x00010000); + nvkm_wr32(device, 0x61020c + (ctrl * 0x0010), ctrl); + nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000010, 0x00000010); + nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put); + nvkm_wr32(device, 0x610200 + (ctrl * 0x0010), 0x00000013); + + /* wait for it to go inactive */ + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x80000000)) + break; + ) < 0) { + nvkm_error(subdev, "ch %d init timeout, %08x\n", user, + nvkm_rd32(device, 0x610200 + (ctrl * 0x10))); + return -EBUSY; + } + + return 0; +} + +int +nv50_disp_dmac_push(struct nvkm_disp_chan *chan, u64 object) +{ + chan->memory = nvkm_umem_search(chan->object.client, object); + if (IS_ERR(chan->memory)) + return PTR_ERR(chan->memory); + + if (nvkm_memory_size(chan->memory) < 0x1000) + return -EINVAL; + + switch (nvkm_memory_target(chan->memory)) { + case NVKM_MEM_TARGET_VRAM: chan->push = 0x00000001; break; + case NVKM_MEM_TARGET_NCOH: chan->push = 0x00000002; break; + case NVKM_MEM_TARGET_HOST: chan->push = 0x00000003; break; + default: + return -EINVAL; + } + + chan->push |= nvkm_memory_addr(chan->memory) >> 8; + return 0; +} + +const struct nvkm_disp_chan_func +nv50_disp_dmac_func = { + .push = nv50_disp_dmac_push, + .init = nv50_disp_dmac_init, + .fini = nv50_disp_dmac_fini, + .intr = nv50_disp_chan_intr, + .user = nv50_disp_chan_user, + .bind = nv50_disp_dmac_bind, +}; + +const struct nvkm_disp_chan_user +nv50_disp_curs = { + .func = &nv50_disp_pioc_func, + .ctrl = 7, + .user = 7, +}; + +const struct nvkm_disp_chan_user +nv50_disp_oimm = { + .func = &nv50_disp_pioc_func, + .ctrl = 5, + .user = 5, +}; + +static const struct nvkm_disp_mthd_list +nv50_disp_ovly_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x000000 }, + { 0x0084, 0x0009a0 }, + { 0x0088, 0x0009c0 }, + { 0x008c, 0x0009c8 }, + { 0x0090, 0x6109b4 }, + { 0x0094, 0x610970 }, + { 0x00a0, 0x610998 }, + { 0x00a4, 0x610964 }, + { 0x00c0, 0x610958 }, + { 0x00e0, 0x6109a8 }, + { 0x00e4, 0x6109d0 }, + { 0x00e8, 0x6109d8 }, + { 0x0100, 0x61094c }, + { 0x0104, 0x610984 }, + { 0x0108, 0x61098c }, + { 0x0800, 0x6109f8 }, + { 0x0808, 0x610a08 }, + { 0x080c, 0x610a10 }, + { 0x0810, 0x610a00 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +nv50_disp_ovly_mthd = { + .name = "Overlay", + .addr = 0x000540, + .prev = 0x000004, + .data = { + { "Global", 1, &nv50_disp_ovly_mthd_base }, + {} + } +}; + +static const struct nvkm_disp_chan_user +nv50_disp_ovly = { + .func = &nv50_disp_dmac_func, + .ctrl = 3, + .user = 3, + .mthd = &nv50_disp_ovly_mthd, +}; + +static const struct nvkm_disp_mthd_list +nv50_disp_base_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x000000 }, + { 0x0084, 0x0008c4 }, + { 0x0088, 0x0008d0 }, + { 0x008c, 0x0008dc }, + { 0x0090, 0x0008e4 }, + { 0x0094, 0x610884 }, + { 0x00a0, 0x6108a0 }, + { 0x00a4, 0x610878 }, + { 0x00c0, 0x61086c }, + { 0x00e0, 0x610858 }, + { 0x00e4, 0x610860 }, + { 0x00e8, 0x6108ac }, + { 0x00ec, 0x6108b4 }, + { 0x0100, 0x610894 }, + { 0x0110, 0x6108bc }, + { 0x0114, 0x61088c }, + {} + } +}; + +const struct nvkm_disp_mthd_list +nv50_disp_base_mthd_image = { + .mthd = 0x0400, + .addr = 0x000000, + .data = { + { 0x0800, 0x6108f0 }, + { 0x0804, 0x6108fc }, + { 0x0808, 0x61090c }, + { 0x080c, 0x610914 }, + { 0x0810, 0x610904 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +nv50_disp_base_mthd = { + .name = "Base", + .addr = 0x000540, + .prev = 0x000004, + .data = { + { "Global", 1, &nv50_disp_base_mthd_base }, + { "Image", 2, &nv50_disp_base_mthd_image }, + {} + } +}; + +static const struct nvkm_disp_chan_user +nv50_disp_base = { + .func = &nv50_disp_dmac_func, + .ctrl = 1, + .user = 1, + .mthd = &nv50_disp_base_mthd, +}; + +const struct nvkm_disp_mthd_list +nv50_disp_core_mthd_base = { + .mthd = 0x0000, + .addr = 0x000000, + .data = { + { 0x0080, 0x000000 }, + { 0x0084, 0x610bb8 }, + { 0x0088, 0x610b9c }, + { 0x008c, 0x000000 }, + {} + } +}; + +static const struct nvkm_disp_mthd_list +nv50_disp_core_mthd_dac = { + .mthd = 0x0080, + .addr = 0x000008, + .data = { + { 0x0400, 0x610b58 }, + { 0x0404, 0x610bdc }, + { 0x0420, 0x610828 }, + {} + } +}; + +const struct nvkm_disp_mthd_list +nv50_disp_core_mthd_sor = { + .mthd = 0x0040, + .addr = 0x000008, + .data = { + { 0x0600, 0x610b70 }, + {} + } +}; + +const struct nvkm_disp_mthd_list +nv50_disp_core_mthd_pior = { + .mthd = 0x0040, + .addr = 0x000008, + .data = { + { 0x0700, 0x610b80 }, + {} + } +}; + +static const struct nvkm_disp_mthd_list +nv50_disp_core_mthd_head = { + .mthd = 0x0400, + .addr = 0x000540, + .data = { + { 0x0800, 0x610ad8 }, + { 0x0804, 0x610ad0 }, + { 0x0808, 0x610a48 }, + { 0x080c, 0x610a78 }, + { 0x0810, 0x610ac0 }, + { 0x0814, 0x610af8 }, + { 0x0818, 0x610b00 }, + { 0x081c, 0x610ae8 }, + { 0x0820, 0x610af0 }, + { 0x0824, 0x610b08 }, + { 0x0828, 0x610b10 }, + { 0x082c, 0x610a68 }, + { 0x0830, 0x610a60 }, + { 0x0834, 0x000000 }, + { 0x0838, 0x610a40 }, + { 0x0840, 0x610a24 }, + { 0x0844, 0x610a2c }, + { 0x0848, 0x610aa8 }, + { 0x084c, 0x610ab0 }, + { 0x0860, 0x610a84 }, + { 0x0864, 0x610a90 }, + { 0x0868, 0x610b18 }, + { 0x086c, 0x610b20 }, + { 0x0870, 0x610ac8 }, + { 0x0874, 0x610a38 }, + { 0x0880, 0x610a58 }, + { 0x0884, 0x610a9c }, + { 0x08a0, 0x610a70 }, + { 0x08a4, 0x610a50 }, + { 0x08a8, 0x610ae0 }, + { 0x08c0, 0x610b28 }, + { 0x08c4, 0x610b30 }, + { 0x08c8, 0x610b40 }, + { 0x08d4, 0x610b38 }, + { 0x08d8, 0x610b48 }, + { 0x08dc, 0x610b50 }, + { 0x0900, 0x610a18 }, + { 0x0904, 0x610ab8 }, + {} + } +}; + +static const struct nvkm_disp_chan_mthd +nv50_disp_core_mthd = { + .name = "Core", + .addr = 0x000000, + .prev = 0x000004, + .data = { + { "Global", 1, &nv50_disp_core_mthd_base }, + { "DAC", 3, &nv50_disp_core_mthd_dac }, + { "SOR", 2, &nv50_disp_core_mthd_sor }, + { "PIOR", 3, &nv50_disp_core_mthd_pior }, + { "HEAD", 2, &nv50_disp_core_mthd_head }, + {} + } +}; + +static void +nv50_disp_core_fini(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + + /* deactivate channel */ + nvkm_mask(device, 0x610200, 0x00000010, 0x00000000); + nvkm_mask(device, 0x610200, 0x00000003, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610200) & 0x001e0000)) + break; + ) < 0) { + nvkm_error(subdev, "core fini: %08x\n", + nvkm_rd32(device, 0x610200)); + } + + chan->suspend_put = nvkm_rd32(device, 0x640000); +} + +static int +nv50_disp_core_init(struct nvkm_disp_chan *chan) +{ + struct nvkm_subdev *subdev = &chan->disp->engine.subdev; + struct nvkm_device *device = subdev->device; + + /* attempt to unstick channel from some unknown state */ + if ((nvkm_rd32(device, 0x610200) & 0x009f0000) == 0x00020000) + nvkm_mask(device, 0x610200, 0x00800000, 0x00800000); + if ((nvkm_rd32(device, 0x610200) & 0x003f0000) == 0x00030000) + nvkm_mask(device, 0x610200, 0x00600000, 0x00600000); + + /* initialise channel for dma command submission */ + nvkm_wr32(device, 0x610204, chan->push); + nvkm_wr32(device, 0x610208, 0x00010000); + nvkm_wr32(device, 0x61020c, 0x00000000); + nvkm_mask(device, 0x610200, 0x00000010, 0x00000010); + nvkm_wr32(device, 0x640000, chan->suspend_put); + nvkm_wr32(device, 0x610200, 0x01000013); + + /* wait for it to go inactive */ + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x610200) & 0x80000000)) + break; + ) < 0) { + nvkm_error(subdev, "core init: %08x\n", + nvkm_rd32(device, 0x610200)); + return -EBUSY; + } + + return 0; +} + +const struct nvkm_disp_chan_func +nv50_disp_core_func = { + .push = nv50_disp_dmac_push, + .init = nv50_disp_core_init, + .fini = nv50_disp_core_fini, + .intr = nv50_disp_chan_intr, + .user = nv50_disp_chan_user, + .bind = nv50_disp_dmac_bind, +}; + +static const struct nvkm_disp_chan_user +nv50_disp_core = { + .func = &nv50_disp_core_func, + .ctrl = 0, + .user = 0, + .mthd = &nv50_disp_core_mthd, +}; + +static u32 +nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *iedt) +{ + struct nvkm_bios *bios = head->disp->engine.subdev.device->bios; + const u8 l = ffs(outp->info.link); + const u16 t = outp->info.hasht; + const u16 m = (0x0100 << head->id) | (l << 6) | outp->info.or; + u32 data = nvbios_outp_match(bios, t, m, ver, hdr, cnt, len, iedt); + if (!data) + OUTP_DBG(outp, "missing IEDT for %04x:%04x", t, m); + return data; +} + +static void +nv50_disp_super_ied_on(struct nvkm_head *head, + struct nvkm_ior *ior, int id, u32 khz) +{ + struct nvkm_subdev *subdev = &head->disp->engine.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvkm_outp *outp = ior->asy.outp; + struct nvbios_ocfg iedtrs; + struct nvbios_outp iedt; + u8 ver, hdr, cnt, len, flags = 0x00; + u32 data; + + if (!outp) { + IOR_DBG(ior, "nothing to attach"); + return; + } + + /* Lookup IED table for the device. */ + data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); + if (!data) + return; + + /* Lookup IEDT runtime settings for the current configuration. */ + if (ior->type == SOR) { + if (ior->asy.proto == LVDS) { + if (head->asy.or.depth == 24) + flags |= 0x02; + } + if (ior->asy.link == 3) + flags |= 0x01; + } + + data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags, + &ver, &hdr, &cnt, &len, &iedtrs); + if (!data) { + OUTP_DBG(outp, "missing IEDT RS for %02x:%02x", + ior->asy.proto_evo, flags); + return; + } + + /* Execute the OnInt[23] script for the current frequency. */ + data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz); + if (!data) { + OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz", + id, ior->asy.proto_evo, flags, khz); + return; + } + + nvbios_init(subdev, data, + init.outp = &outp->info; + init.or = ior->id; + init.link = ior->asy.link; + init.head = head->id; + ); +} + +static void +nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id) +{ + struct nvkm_outp *outp = ior->arm.outp; + struct nvbios_outp iedt; + u8 ver, hdr, cnt, len; + u32 data; + + if (!outp) { + IOR_DBG(ior, "nothing attached"); + return; + } + + data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); + if (!data) + return; + + nvbios_init(&head->disp->engine.subdev, iedt.script[id], + init.outp = &outp->info; + init.or = ior->id; + init.link = ior->arm.link; + init.head = head->id; + ); +} + +static struct nvkm_ior * +nv50_disp_super_ior_asy(struct nvkm_head *head) +{ + struct nvkm_ior *ior; + list_for_each_entry(ior, &head->disp->iors, head) { + if (ior->asy.head & (1 << head->id)) { + HEAD_DBG(head, "to %s", ior->name); + return ior; + } + } + HEAD_DBG(head, "nothing to attach"); + return NULL; +} + +static struct nvkm_ior * +nv50_disp_super_ior_arm(struct nvkm_head *head) +{ + struct nvkm_ior *ior; + list_for_each_entry(ior, &head->disp->iors, head) { + if (ior->arm.head & (1 << head->id)) { + HEAD_DBG(head, "on %s", ior->name); + return ior; + } + } + HEAD_DBG(head, "nothing attached"); + return NULL; +} + +void +nv50_disp_super_3_0(struct nvkm_disp *disp, struct nvkm_head *head) +{ + struct nvkm_ior *ior; + + /* Determine which OR, if any, we're attaching to the head. */ + HEAD_DBG(head, "supervisor 3.0"); + ior = nv50_disp_super_ior_asy(head); + if (!ior) + return; + + /* Execute OnInt3 IED script. */ + nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000); + + /* OR-specific handling. */ + if (ior->func->war_3) + ior->func->war_3(ior); +} + +static void +nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior) +{ + struct nvkm_subdev *subdev = &head->disp->engine.subdev; + const u32 khz = head->asy.hz / 1000; + const u32 linkKBps = ior->dp.bw * 27000; + const u32 symbol = 100000; + int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; + int TU, VTUi, VTUf, VTUa; + u64 link_data_rate, link_ratio, unk; + u32 best_diff = 64 * symbol; + u64 h, v; + + /* symbols/hblank - algorithm taken from comments in tegra driver */ + h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7; + h = h * linkKBps; + do_div(h, khz); + h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr); + + /* symbols/vblank - algorithm taken from comments in tegra driver */ + v = head->asy.vblanks - head->asy.vblanke - 25; + v = v * linkKBps; + do_div(v, khz); + v = v - ((36 / ior->dp.nr) + 3) - 1; + + ior->func->dp->audio_sym(ior, head->id, h, v); + + /* watermark / activesym */ + link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr; + + /* calculate ratio of packed data rate to link symbol rate */ + link_ratio = link_data_rate * symbol; + do_div(link_ratio, linkKBps); + + for (TU = 64; ior->func->dp->activesym && TU >= 32; TU--) { + /* calculate average number of valid symbols in each TU */ + u32 tu_valid = link_ratio * TU; + u32 calc, diff; + + /* find a hw representation for the fraction.. */ + VTUi = tu_valid / symbol; + calc = VTUi * symbol; + diff = tu_valid - calc; + if (diff) { + if (diff >= (symbol / 2)) { + VTUf = symbol / (symbol - diff); + if (symbol - (VTUf * diff)) + VTUf++; + + if (VTUf <= 15) { + VTUa = 1; + calc += symbol - (symbol / VTUf); + } else { + VTUa = 0; + VTUf = 1; + calc += symbol; + } + } else { + VTUa = 0; + VTUf = min((int)(symbol / diff), 15); + calc += symbol / VTUf; + } + + diff = calc - tu_valid; + } else { + /* no remainder, but the hw doesn't like the fractional + * part to be zero. decrement the integer part and + * have the fraction add a whole symbol back + */ + VTUa = 0; + VTUf = 1; + VTUi--; + } + + if (diff < best_diff) { + best_diff = diff; + bestTU = TU; + bestVTUa = VTUa; + bestVTUf = VTUf; + bestVTUi = VTUi; + if (diff == 0) + break; + } + } + + if (ior->func->dp->activesym) { + if (!bestTU) { + nvkm_error(subdev, "unable to determine dp config\n"); + return; + } + + ior->func->dp->activesym(ior, head->id, bestTU, bestVTUa, bestVTUf, bestVTUi); + } else { + bestTU = 64; + } + + /* XXX close to vbios numbers, but not right */ + unk = (symbol - link_ratio) * bestTU; + unk *= link_ratio; + do_div(unk, symbol); + do_div(unk, symbol); + unk += 6; + + ior->func->dp->watermark(ior, head->id, unk); +} + +void +nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head) +{ + const u32 khz = head->asy.hz / 1000; + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + /* Determine which OR, if any, we're attaching from the head. */ + HEAD_DBG(head, "supervisor 2.2"); + ior = nv50_disp_super_ior_asy(head); + if (!ior) + return; + + /* For some reason, NVIDIA decided not to: + * + * A) Give dual-link LVDS a separate EVO protocol, like for TMDS. + * and + * B) Use SetControlOutputResource.PixelDepth on LVDS. + * + * Override the values we usually read from HW with the same + * data we pass though an ioctl instead. + */ + if (ior->type == SOR && ior->asy.proto == LVDS) { + head->asy.or.depth = (disp->sor.lvdsconf & 0x0200) ? 24 : 18; + ior->asy.link = (disp->sor.lvdsconf & 0x0100) ? 3 : 1; + } + + /* Handle any link training, etc. */ + if ((outp = ior->asy.outp) && outp->func->acquire) + outp->func->acquire(outp); + + /* Execute OnInt2 IED script. */ + nv50_disp_super_ied_on(head, ior, 0, khz); + + /* Program RG clock divider. */ + head->func->rgclk(head, ior->asy.rgdiv); + + /* Mode-specific internal DP configuration. */ + if (ior->type == SOR && ior->asy.proto == DP) + nv50_disp_super_2_2_dp(head, ior); + + /* OR-specific handling. */ + ior->func->clock(ior); + if (ior->func->war_2) + ior->func->war_2(ior); +} + +void +nv50_disp_super_2_1(struct nvkm_disp *disp, struct nvkm_head *head) +{ + struct nvkm_devinit *devinit = disp->engine.subdev.device->devinit; + const u32 khz = head->asy.hz / 1000; + HEAD_DBG(head, "supervisor 2.1 - %d khz", khz); + if (khz) + nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz); +} + +void +nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head) +{ + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + /* Determine which OR, if any, we're detaching from the head. */ + HEAD_DBG(head, "supervisor 2.0"); + ior = nv50_disp_super_ior_arm(head); + if (!ior) + return; + + /* Execute OffInt2 IED script. */ + nv50_disp_super_ied_off(head, ior, 2); + + /* If we're shutting down the OR's only active head, execute + * the output path's disable function. + */ + if (ior->arm.head == (1 << head->id)) { + if ((outp = ior->arm.outp) && outp->func->disable) + outp->func->disable(outp, ior); + } +} + +void +nv50_disp_super_1_0(struct nvkm_disp *disp, struct nvkm_head *head) +{ + struct nvkm_ior *ior; + + /* Determine which OR, if any, we're detaching from the head. */ + HEAD_DBG(head, "supervisor 1.0"); + ior = nv50_disp_super_ior_arm(head); + if (!ior) + return; + + /* Execute OffInt1 IED script. */ + nv50_disp_super_ied_off(head, ior, 1); +} + +void +nv50_disp_super_1(struct nvkm_disp *disp) +{ + struct nvkm_head *head; + struct nvkm_ior *ior; + + list_for_each_entry(head, &disp->heads, head) { + head->func->state(head, &head->arm); + head->func->state(head, &head->asy); + } + + list_for_each_entry(ior, &disp->iors, head) { + ior->func->state(ior, &ior->arm); + ior->func->state(ior, &ior->asy); + } +} + +void +nv50_disp_super(struct work_struct *work) +{ + struct nvkm_disp *disp = container_of(work, struct nvkm_disp, super.work); + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_head *head; + u32 super; + + mutex_lock(&disp->super.mutex); + super = nvkm_rd32(device, 0x610030); + + nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super.pending, super); + + if (disp->super.pending & 0x00000010) { + nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); + nv50_disp_super_1(disp); + list_for_each_entry(head, &disp->heads, head) { + if (!(super & (0x00000020 << head->id))) + continue; + if (!(super & (0x00000080 << head->id))) + continue; + nv50_disp_super_1_0(disp, head); + } + } else + if (disp->super.pending & 0x00000020) { + list_for_each_entry(head, &disp->heads, head) { + if (!(super & (0x00000080 << head->id))) + continue; + nv50_disp_super_2_0(disp, head); + } + nvkm_outp_route(disp); + list_for_each_entry(head, &disp->heads, head) { + if (!(super & (0x00000200 << head->id))) + continue; + nv50_disp_super_2_1(disp, head); + } + list_for_each_entry(head, &disp->heads, head) { + if (!(super & (0x00000080 << head->id))) + continue; + nv50_disp_super_2_2(disp, head); + } + } else + if (disp->super.pending & 0x00000040) { + list_for_each_entry(head, &disp->heads, head) { + if (!(super & (0x00000080 << head->id))) + continue; + nv50_disp_super_3_0(disp, head); + } + } + + nvkm_wr32(device, 0x610030, 0x80000000); + mutex_unlock(&disp->super.mutex); +} + +const struct nvkm_enum +nv50_disp_intr_error_type[] = { + { 0, "NONE" }, + { 1, "PUSHBUFFER_ERR" }, + { 2, "TRAP" }, + { 3, "RESERVED_METHOD" }, + { 4, "INVALID_ARG" }, + { 5, "INVALID_STATE" }, + { 7, "UNRESOLVABLE_HANDLE" }, + {} +}; + +static const struct nvkm_enum +nv50_disp_intr_error_code[] = { + { 0x00, "" }, + {} +}; + +static void +nv50_disp_intr_error(struct nvkm_disp *disp, int chid) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08)); + u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08)); + u32 code = (addr & 0x00ff0000) >> 16; + u32 type = (addr & 0x00007000) >> 12; + u32 mthd = (addr & 0x00000ffc); + const struct nvkm_enum *ec, *et; + + et = nvkm_enum_find(nv50_disp_intr_error_type, type); + ec = nvkm_enum_find(nv50_disp_intr_error_code, code); + + nvkm_error(subdev, + "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n", + type, et ? et->name : "", code, ec ? ec->name : "", + chid, mthd, data); + + if (chid < ARRAY_SIZE(disp->chan)) { + switch (mthd) { + case 0x0080: + nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); + break; + default: + break; + } + } + + nvkm_wr32(device, 0x610020, 0x00010000 << chid); + nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000); +} + +void +nv50_disp_intr(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + u32 intr0 = nvkm_rd32(device, 0x610020); + u32 intr1 = nvkm_rd32(device, 0x610024); + + while (intr0 & 0x001f0000) { + u32 chid = __ffs(intr0 & 0x001f0000) - 16; + nv50_disp_intr_error(disp, chid); + intr0 &= ~(0x00010000 << chid); + } + + while (intr0 & 0x0000001f) { + u32 chid = __ffs(intr0 & 0x0000001f); + nv50_disp_chan_uevent_send(disp, chid); + intr0 &= ~(0x00000001 << chid); + } + + if (intr1 & 0x00000004) { + nvkm_disp_vblank(disp, 0); + nvkm_wr32(device, 0x610024, 0x00000004); + } + + if (intr1 & 0x00000008) { + nvkm_disp_vblank(disp, 1); + nvkm_wr32(device, 0x610024, 0x00000008); + } + + if (intr1 & 0x00000070) { + disp->super.pending = (intr1 & 0x00000070); + queue_work(disp->super.wq, &disp->super.work); + nvkm_wr32(device, 0x610024, disp->super.pending); + } +} + +void +nv50_disp_fini(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + /* disable all interrupts */ + nvkm_wr32(device, 0x610024, 0x00000000); + nvkm_wr32(device, 0x610020, 0x00000000); +} + +int +nv50_disp_init(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_head *head; + u32 tmp; + int i; + + /* The below segments of code copying values from one register to + * another appear to inform EVO of the display capabilities or + * something similar. NFI what the 0x614004 caps are for.. + */ + tmp = nvkm_rd32(device, 0x614004); + nvkm_wr32(device, 0x610184, tmp); + + /* ... CRTC caps */ + list_for_each_entry(head, &disp->heads, head) { + tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800)); + nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp); + tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800)); + nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp); + tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800)); + nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp); + tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800)); + nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp); + } + + /* ... DAC caps */ + for (i = 0; i < disp->dac.nr; i++) { + tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800)); + nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp); + } + + /* ... SOR caps */ + for (i = 0; i < disp->sor.nr; i++) { + tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); + nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp); + } + + /* ... PIOR caps */ + for (i = 0; i < disp->pior.nr; i++) { + tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800)); + nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp); + } + + /* steal display away from vbios, or something like that */ + if (nvkm_rd32(device, 0x610024) & 0x00000100) { + nvkm_wr32(device, 0x610024, 0x00000100); + nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002)) + break; + ) < 0) + return -EBUSY; + } + + /* point at display engine memory area (hash table, objects) */ + nvkm_wr32(device, 0x610010, (disp->inst->addr >> 8) | 9); + + /* enable supervisor interrupts, disable everything else */ + nvkm_wr32(device, 0x61002c, 0x00000370); + nvkm_wr32(device, 0x610028, 0x00000000); + return 0; +} + +int +nv50_disp_oneinit(struct nvkm_disp *disp) +{ + const struct nvkm_disp_func *func = disp->func; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + int ret, i; + + if (func->wndw.cnt) { + disp->wndw.nr = func->wndw.cnt(disp, &disp->wndw.mask); + nvkm_debug(subdev, "Window(s): %d (%08lx)\n", disp->wndw.nr, disp->wndw.mask); + } + + disp->head.nr = func->head.cnt(disp, &disp->head.mask); + nvkm_debug(subdev, " Head(s): %d (%02lx)\n", disp->head.nr, disp->head.mask); + for_each_set_bit(i, &disp->head.mask, disp->head.nr) { + ret = func->head.new(disp, i); + if (ret) + return ret; + } + + if (func->dac.cnt) { + disp->dac.nr = func->dac.cnt(disp, &disp->dac.mask); + nvkm_debug(subdev, " DAC(s): %d (%02lx)\n", disp->dac.nr, disp->dac.mask); + for_each_set_bit(i, &disp->dac.mask, disp->dac.nr) { + ret = func->dac.new(disp, i); + if (ret) + return ret; + } + } + + if (func->pior.cnt) { + disp->pior.nr = func->pior.cnt(disp, &disp->pior.mask); + nvkm_debug(subdev, " PIOR(s): %d (%02lx)\n", disp->pior.nr, disp->pior.mask); + for_each_set_bit(i, &disp->pior.mask, disp->pior.nr) { + ret = func->pior.new(disp, i); + if (ret) + return ret; + } + } + + disp->sor.nr = func->sor.cnt(disp, &disp->sor.mask); + nvkm_debug(subdev, " SOR(s): %d (%02lx)\n", disp->sor.nr, disp->sor.mask); + for_each_set_bit(i, &disp->sor.mask, disp->sor.nr) { + ret = func->sor.new(disp, i); + if (ret) + return ret; + } + + ret = nvkm_gpuobj_new(device, 0x10000, 0x10000, false, NULL, &disp->inst); + if (ret) + return ret; + + return nvkm_ramht_new(device, func->ramht_size ? func->ramht_size : + 0x1000, 0, disp->inst, &disp->ramht); +} + +static const struct nvkm_disp_func +nv50_disp = { + .oneinit = nv50_disp_oneinit, + .init = nv50_disp_init, + .fini = nv50_disp_fini, + .intr = nv50_disp_intr, + .super = nv50_disp_super, + .uevent = &nv50_disp_chan_uevent, + .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, + .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, + .sor = { .cnt = nv50_sor_cnt, .new = nv50_sor_new }, + .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, + .root = { 0, 0, NV50_DISP }, + .user = { + {{0,0,NV50_DISP_CURSOR }, nvkm_disp_chan_new, &nv50_disp_curs }, + {{0,0,NV50_DISP_OVERLAY }, nvkm_disp_chan_new, &nv50_disp_oimm }, + {{0,0,NV50_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &nv50_disp_base }, + {{0,0,NV50_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &nv50_disp_core }, + {{0,0,NV50_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, &nv50_disp_ovly }, + {} + } +}; + +int +nv50_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&nv50_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c new file mode 100644 index 000000000..6094805fb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -0,0 +1,338 @@ +/* + * Copyright 2014 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 "outp.h" +#include "dp.h" +#include "ior.h" + +#include +#include +#include + +void +nvkm_outp_route(struct nvkm_disp *disp) +{ + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + list_for_each_entry(ior, &disp->iors, head) { + if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) { + OUTP_DBG(outp, "release %s", ior->name); + if (ior->func->route.set) + ior->func->route.set(outp, NULL); + ior->arm.outp = NULL; + } + } + + list_for_each_entry(ior, &disp->iors, head) { + if ((outp = ior->asy.outp)) { + OUTP_DBG(outp, "acquire %s", ior->name); + if (ior->asy.outp != ior->arm.outp) { + if (ior->func->route.set) + ior->func->route.set(outp, ior); + ior->arm.outp = ior->asy.outp; + } + } + } +} + +static enum nvkm_ior_proto +nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) +{ + switch (outp->info.location) { + case 0: + switch (outp->info.type) { + case DCB_OUTPUT_ANALOG: *type = DAC; return CRT; + case DCB_OUTPUT_TV : *type = DAC; return TV; + case DCB_OUTPUT_TMDS : *type = SOR; return TMDS; + case DCB_OUTPUT_LVDS : *type = SOR; return LVDS; + case DCB_OUTPUT_DP : *type = SOR; return DP; + default: + break; + } + break; + case 1: + switch (outp->info.type) { + case DCB_OUTPUT_TMDS: *type = PIOR; return TMDS; + case DCB_OUTPUT_DP : *type = PIOR; return TMDS; /* not a bug */ + default: + break; + } + break; + default: + break; + } + WARN_ON(1); + return UNKNOWN; +} + +void +nvkm_outp_release(struct nvkm_outp *outp, u8 user) +{ + struct nvkm_ior *ior = outp->ior; + OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior); + if (ior) { + outp->acquired &= ~user; + if (!outp->acquired) { + if (outp->func->release && outp->ior) + outp->func->release(outp); + outp->ior->asy.outp = NULL; + outp->ior = NULL; + } + } +} + +static inline int +nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior) +{ + outp->ior = ior; + outp->ior->asy.outp = outp; + outp->ior->asy.link = outp->info.sorconf.link; + outp->acquired |= user; + return 0; +} + +static inline int +nvkm_outp_acquire_hda(struct nvkm_outp *outp, enum nvkm_ior_type type, + u8 user, bool hda) +{ + struct nvkm_ior *ior; + + /* Failing that, a completely unused OR is the next best thing. */ + list_for_each_entry(ior, &outp->disp->iors, head) { + if (!ior->identity && ior->hda == hda && + !ior->asy.outp && ior->type == type && !ior->arm.outp && + (ior->func->route.set || ior->id == __ffs(outp->info.or))) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + /* Last resort is to assign an OR that's already active on HW, + * but will be released during the next modeset. + */ + list_for_each_entry(ior, &outp->disp->iors, head) { + if (!ior->identity && ior->hda == hda && + !ior->asy.outp && ior->type == type && + (ior->func->route.set || ior->id == __ffs(outp->info.or))) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + return -ENOSPC; +} + +int +nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda) +{ + struct nvkm_ior *ior = outp->ior; + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + + OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior); + if (ior) { + outp->acquired |= user; + return 0; + } + + /* Lookup a compatible, and unused, OR to assign to the device. */ + proto = nvkm_outp_xlat(outp, &type); + if (proto == UNKNOWN) + return -ENOSYS; + + /* Deal with panels requiring identity-mapped SOR assignment. */ + if (outp->identity) { + ior = nvkm_ior_find(outp->disp, SOR, ffs(outp->info.or) - 1); + if (WARN_ON(!ior)) + return -ENOSPC; + return nvkm_outp_acquire_ior(outp, user, ior); + } + + /* First preference is to reuse the OR that is currently armed + * on HW, if any, in order to prevent unnecessary switching. + */ + list_for_each_entry(ior, &outp->disp->iors, head) { + if (!ior->identity && !ior->asy.outp && ior->arm.outp == outp) { + /*XXX: For various complicated reasons, we can't outright switch + * the boot-time OR on the first modeset without some fairly + * invasive changes. + * + * The systems that were fixed by modifying the OR selection + * code to account for HDA support shouldn't regress here as + * the HDA-enabled ORs match the relevant output's pad macro + * index, and the firmware seems to select an OR this way. + * + * This warning is to make it obvious if that proves wrong. + */ + WARN_ON(hda && !ior->hda); + return nvkm_outp_acquire_ior(outp, user, ior); + } + } + + /* If we don't need HDA, first try to acquire an OR that doesn't + * support it to leave free the ones that do. + */ + if (!hda) { + if (!nvkm_outp_acquire_hda(outp, type, user, false)) + return 0; + + /* Use a HDA-supporting SOR anyway. */ + return nvkm_outp_acquire_hda(outp, type, user, true); + } + + /* We want HDA, try to acquire an OR that supports it. */ + if (!nvkm_outp_acquire_hda(outp, type, user, true)) + return 0; + + /* There weren't any free ORs that support HDA, grab one that + * doesn't and at least allow display to work still. + */ + return nvkm_outp_acquire_hda(outp, type, user, false); +} + +void +nvkm_outp_fini(struct nvkm_outp *outp) +{ + if (outp->func->fini) + outp->func->fini(outp); +} + +static void +nvkm_outp_init_route(struct nvkm_outp *outp) +{ + struct nvkm_disp *disp = outp->disp; + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + struct nvkm_ior *ior; + int id, link; + + /* Find any OR from the class that is able to support this device. */ + proto = nvkm_outp_xlat(outp, &type); + if (proto == UNKNOWN) + return; + + ior = nvkm_ior_find(disp, type, -1); + if (!ior) { + WARN_ON(1); + return; + } + + /* Determine the specific OR, if any, this device is attached to. */ + if (ior->func->route.get) { + id = ior->func->route.get(outp, &link); + if (id < 0) { + OUTP_DBG(outp, "no route"); + return; + } + } else { + /* Prior to DCB 4.1, this is hardwired like so. */ + id = ffs(outp->info.or) - 1; + link = (ior->type == SOR) ? outp->info.sorconf.link : 0; + } + + ior = nvkm_ior_find(disp, type, id); + if (!ior) { + WARN_ON(1); + return; + } + + /* Determine if the OR is already configured for this device. */ + ior->func->state(ior, &ior->arm); + if (!ior->arm.head || ior->arm.proto != proto) { + OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head, + ior->arm.proto, proto); + + /* The EFI GOP driver on Ampere can leave unused DP links routed, + * which we don't expect. The DisableLT IED script *should* get + * us back to where we need to be. + */ + if (ior->func->route.get && !ior->arm.head && outp->info.type == DCB_OUTPUT_DP) + nvkm_dp_disable(outp, ior); + + return; + } + + OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link); + ior->arm.outp = outp; +} + +void +nvkm_outp_init(struct nvkm_outp *outp) +{ + nvkm_outp_init_route(outp); + if (outp->func->init) + outp->func->init(outp); +} + +void +nvkm_outp_del(struct nvkm_outp **poutp) +{ + struct nvkm_outp *outp = *poutp; + if (outp && !WARN_ON(!outp->func)) { + if (outp->func->dtor) + *poutp = outp->func->dtor(outp); + kfree(*poutp); + *poutp = NULL; + } +} + +int +nvkm_outp_new_(const struct nvkm_outp_func *func, struct nvkm_disp *disp, + int index, struct dcb_output *dcbE, struct nvkm_outp **poutp) +{ + struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; + struct nvkm_outp *outp; + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + + if (!(outp = *poutp = kzalloc(sizeof(*outp), GFP_KERNEL))) + return -ENOMEM; + + outp->func = func; + outp->disp = disp; + outp->index = index; + outp->info = *dcbE; + outp->i2c = nvkm_i2c_bus_find(i2c, dcbE->i2c_index); + + OUTP_DBG(outp, "type %02x loc %d or %d link %d con %x " + "edid %x bus %d head %x", + outp->info.type, outp->info.location, outp->info.or, + outp->info.type >= 2 ? outp->info.sorconf.link : 0, + outp->info.connector, outp->info.i2c_index, + outp->info.bus, outp->info.heads); + + /* Cull output paths we can't map to an output resource. */ + proto = nvkm_outp_xlat(outp, &type); + if (proto == UNKNOWN) + return -ENODEV; + + return 0; +} + +static const struct nvkm_outp_func +nvkm_outp = { +}; + +int +nvkm_outp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, + struct nvkm_outp **poutp) +{ + return nvkm_outp_new_(&nvkm_outp, disp, index, dcbE, poutp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h new file mode 100644 index 000000000..3f3924c41 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_OUTP_H__ +#define __NVKM_DISP_OUTP_H__ +#include "priv.h" +#include + +#include +#include +#include + +struct nvkm_outp { + const struct nvkm_outp_func *func; + struct nvkm_disp *disp; + int index; + struct dcb_output info; + + struct nvkm_i2c_bus *i2c; + + struct list_head head; + struct nvkm_conn *conn; + bool identity; + + /* Assembly state. */ +#define NVKM_OUTP_PRIV 1 +#define NVKM_OUTP_USER 2 + u8 acquired:2; + struct nvkm_ior *ior; + + union { + struct { + struct nvbios_dpout info; + u8 version; + + struct nvkm_i2c_aux *aux; + + struct nvkm_notify hpd; + bool present; + u8 lttpr[6]; + u8 lttprs; + u8 dpcd[16]; + + struct { + int dpcd; /* -1, or index into SUPPORTED_LINK_RATES table */ + u32 rate; + } rate[8]; + int rates; + int links; + + struct mutex mutex; + struct { + atomic_t done; + bool mst; + } lt; + } dp; + }; + + struct nvkm_object object; +}; + +int nvkm_outp_new_(const struct nvkm_outp_func *, struct nvkm_disp *, int index, + struct dcb_output *, struct nvkm_outp **); +int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *, struct nvkm_outp **); +void nvkm_outp_del(struct nvkm_outp **); +void nvkm_outp_init(struct nvkm_outp *); +void nvkm_outp_fini(struct nvkm_outp *); +int nvkm_outp_acquire(struct nvkm_outp *, u8 user, bool hda); +void nvkm_outp_release(struct nvkm_outp *, u8 user); +void nvkm_outp_route(struct nvkm_disp *); + +struct nvkm_outp_func { + void *(*dtor)(struct nvkm_outp *); + void (*init)(struct nvkm_outp *); + void (*fini)(struct nvkm_outp *); + int (*acquire)(struct nvkm_outp *); + void (*release)(struct nvkm_outp *); + void (*disable)(struct nvkm_outp *, struct nvkm_ior *); +}; + +#define OUTP_MSG(o,l,f,a...) do { \ + struct nvkm_outp *_outp = (o); \ + nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \ + _outp->index, _outp->info.hasht, _outp->info.hashm, ##a); \ +} while(0) +#define OUTP_ERR(o,f,a...) OUTP_MSG((o), error, f, ##a) +#define OUTP_DBG(o,f,a...) OUTP_MSG((o), debug, f, ##a) +#define OUTP_TRACE(o,f,a...) OUTP_MSG((o), trace, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h new file mode 100644 index 000000000..cb25dfe84 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DISP_PRIV_H__ +#define __NVKM_DISP_PRIV_H__ +#define nvkm_udisp(p) container_of((p), struct nvkm_disp, client.object) +#include +#include +struct nvkm_head; +struct nvkm_outp; +struct dcb_output; + +int nvkm_disp_ctor(const struct nvkm_disp_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_disp *); +int nvkm_disp_new_(const struct nvkm_disp_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_disp **); +void nvkm_disp_vblank(struct nvkm_disp *, int head); + +struct nvkm_disp_func { + int (*oneinit)(struct nvkm_disp *); + int (*init)(struct nvkm_disp *); + void (*fini)(struct nvkm_disp *); + void (*intr)(struct nvkm_disp *); + void (*intr_error)(struct nvkm_disp *, int chid); + + void (*super)(struct work_struct *); + + const struct nvkm_event_func *uevent; + + struct { + int (*cnt)(struct nvkm_disp *, unsigned long *mask); + int (*new)(struct nvkm_disp *, int id); + } wndw, head, dac, sor, pior; + + u16 ramht_size; + + const struct nvkm_sclass root; + + struct nvkm_disp_user { + struct nvkm_sclass base; + int (*ctor)(const struct nvkm_oclass *, void *argv, u32 argc, + struct nvkm_object **); + const struct nvkm_disp_chan_user *chan; + } user[]; +}; + +int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **); +int nv04_disp_mthd(struct nvkm_object *, u32, void *, u32); +int nv50_disp_root_mthd_(struct nvkm_object *, u32, void *, u32); + +int nv50_disp_oneinit(struct nvkm_disp *); +int nv50_disp_init(struct nvkm_disp *); +void nv50_disp_fini(struct nvkm_disp *); +void nv50_disp_intr(struct nvkm_disp *); +extern const struct nvkm_enum nv50_disp_intr_error_type[]; +void nv50_disp_super(struct work_struct *); +void nv50_disp_super_1(struct nvkm_disp *); +void nv50_disp_super_1_0(struct nvkm_disp *, struct nvkm_head *); +void nv50_disp_super_2_0(struct nvkm_disp *, struct nvkm_head *); +void nv50_disp_super_2_1(struct nvkm_disp *, struct nvkm_head *); +void nv50_disp_super_2_2(struct nvkm_disp *, struct nvkm_head *); +void nv50_disp_super_3_0(struct nvkm_disp *, struct nvkm_head *); + +int gf119_disp_init(struct nvkm_disp *); +void gf119_disp_fini(struct nvkm_disp *); +void gf119_disp_intr(struct nvkm_disp *); +void gf119_disp_super(struct work_struct *); +void gf119_disp_intr_error(struct nvkm_disp *, int); + +void gv100_disp_fini(struct nvkm_disp *); +void gv100_disp_intr(struct nvkm_disp *); +void gv100_disp_super(struct work_struct *); +int gv100_disp_wndw_cnt(struct nvkm_disp *, unsigned long *); +int gv100_disp_caps_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); + +int tu102_disp_init(struct nvkm_disp *); + +void nv50_disp_dptmds_war_2(struct nvkm_disp *, struct dcb_output *); +void nv50_disp_dptmds_war_3(struct nvkm_disp *, struct dcb_output *); +void nv50_disp_update_sppll1(struct nvkm_disp *); + +extern const struct nvkm_event_func nv50_disp_chan_uevent; +void nv50_disp_chan_uevent_send(struct nvkm_disp *, int); + +extern const struct nvkm_event_func gf119_disp_chan_uevent; +extern const struct nvkm_event_func gv100_disp_chan_uevent; + +int nvkm_udisp_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); +int nvkm_uconn_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); +int nvkm_uoutp_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c new file mode 100644 index 000000000..9acaec5c2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c @@ -0,0 +1,62 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "head.h" + +#include + +#include +#include + +int +nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_disp *disp = nvkm_disp(object->engine); + union { + struct nv04_disp_mthd_v0 v0; + } *args = data; + struct nvkm_head *head; + int id, ret = -ENOSYS; + + nvif_ioctl(object, "disp mthd size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", + args->v0.version, args->v0.method, args->v0.head); + mthd = args->v0.method; + id = args->v0.head; + } else + return ret; + + if (!(head = nvkm_head_find(disp, id))) + return -ENXIO; + + switch (mthd) { + case NV04_DISP_SCANOUTPOS: + return nvkm_head_mthd_scanoutpos(object, head, data, size); + default: + break; + } + + return -EINVAL; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c new file mode 100644 index 000000000..0af45ccd1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c @@ -0,0 +1,250 @@ +/* + * 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 "chan.h" +#include "head.h" +#include "ior.h" +#include "outp.h" + +#include + +#include +#include +#include + +int +nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + union { + struct nv50_disp_mthd_v0 v0; + struct nv50_disp_mthd_v1 v1; + } *args = data; + struct nvkm_disp *disp = nvkm_udisp(object); + struct nvkm_outp *temp, *outp = NULL; + struct nvkm_head *head; + u16 type, mask = 0; + int hidx, ret = -ENOSYS; + + if (mthd != NV50_DISP_MTHD) + return -EINVAL; + + nvif_ioctl(object, "disp mthd size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", + args->v0.version, args->v0.method, args->v0.head); + mthd = args->v0.method; + hidx = args->v0.head; + } else + if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) { + nvif_ioctl(object, "disp mthd vers %d mthd %02x " + "type %04x mask %04x\n", + args->v1.version, args->v1.method, + args->v1.hasht, args->v1.hashm); + mthd = args->v1.method; + type = args->v1.hasht; + mask = args->v1.hashm; + hidx = ffs((mask >> 8) & 0x0f) - 1; + } else + return ret; + + if (!(head = nvkm_head_find(disp, hidx))) + return -ENXIO; + + if (mask) { + list_for_each_entry(temp, &disp->outps, head) { + if ((temp->info.hasht == type) && + (temp->info.hashm & mask) == mask) { + outp = temp; + break; + } + } + if (outp == NULL) + return -ENXIO; + } + + switch (mthd) { + case NV50_DISP_SCANOUTPOS: { + return nvkm_head_mthd_scanoutpos(object, head, data, size); + } + default: + break; + } + + switch (mthd * !!outp) { + case NV50_DISP_MTHD_V1_ACQUIRE: { + union { + struct nv50_disp_acquire_v0 v0; + } *args = data; + int ret = -ENOSYS; + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, args->v0.hda); + if (ret == 0) { + args->v0.or = outp->ior->id; + args->v0.link = outp->ior->asy.link; + } + } + return ret; + } + break; + case NV50_DISP_MTHD_V1_RELEASE: + nvkm_outp_release(outp, NVKM_OUTP_USER); + return 0; + case NV50_DISP_MTHD_V1_SOR_HDA_ELD: { + union { + struct nv50_disp_sor_hda_eld_v0 v0; + } *args = data; + struct nvkm_ior *ior = outp->ior; + int ret = -ENOSYS; + + nvif_ioctl(object, "disp sor hda eld size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "disp sor hda eld vers %d\n", + args->v0.version); + if (size > 0x60) + return -E2BIG; + } else + return ret; + + if (!ior->hda) + return -ENODEV; + + if (size && args->v0.data[0]) { + if (outp->info.type == DCB_OUTPUT_DP) + ior->func->dp->audio(ior, hidx, true); + ior->func->hda->hpd(ior, hidx, true); + ior->func->hda->eld(ior, hidx, data, size); + } else { + if (outp->info.type == DCB_OUTPUT_DP) + ior->func->dp->audio(ior, hidx, false); + ior->func->hda->hpd(ior, hidx, false); + } + + return 0; + } + break; + case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: { + union { + struct nv50_disp_sor_hdmi_pwr_v0 v0; + } *args = data; + u8 *vendor, vendor_size; + u8 *avi, avi_size; + int ret = -ENOSYS; + + nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " + "max_ac_packet %d rekey %d scdc %d\n", + args->v0.version, args->v0.state, + args->v0.max_ac_packet, args->v0.rekey, + args->v0.scdc); + if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) + return -EINVAL; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -EINVAL; + else + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + avi = data; + avi_size = args->v0.avi_infoframe_length; + vendor = avi + avi_size; + vendor_size = args->v0.vendor_infoframe_length; + } else + return ret; + + if (!outp->ior->func->hdmi.ctrl) + return -ENODEV; + + outp->ior->func->hdmi.ctrl(outp->ior, hidx, args->v0.state, + args->v0.max_ac_packet, + args->v0.rekey, avi, avi_size, + vendor, vendor_size); + + if (outp->ior->func->hdmi.scdc) + outp->ior->func->hdmi.scdc(outp->ior, args->v0.scdc); + + return 0; + } + break; + case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: { + union { + struct nv50_disp_sor_lvds_script_v0 v0; + } *args = data; + int ret = -ENOSYS; + nvif_ioctl(object, "disp sor lvds script size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "disp sor lvds script " + "vers %d name %04x\n", + args->v0.version, args->v0.script); + disp->sor.lvdsconf = args->v0.script; + return 0; + } else + return ret; + } + break; + case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: { + union { + struct nv50_disp_sor_dp_mst_link_v0 v0; + } *args = data; + int ret = -ENOSYS; + nvif_ioctl(object, "disp sor dp mst link size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n", + args->v0.version, args->v0.state); + outp->dp.lt.mst = !!args->v0.state; + return 0; + } else + return ret; + } + break; + case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: { + union { + struct nv50_disp_sor_dp_mst_vcpi_v0 v0; + } *args = data; + int ret = -ENOSYS; + nvif_ioctl(object, "disp sor dp mst vcpi size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "disp sor dp mst vcpi vers %d " + "slot %02x/%02x pbn %04x/%04x\n", + args->v0.version, args->v0.start_slot, + args->v0.num_slots, args->v0.pbn, + args->v0.aligned_pbn); + if (!outp->ior->func->dp->vcpi) + return -ENODEV; + outp->ior->func->dp->vcpi(outp->ior, hidx, + args->v0.start_slot, + args->v0.num_slots, + args->v0.pbn, + args->v0.aligned_pbn); + return 0; + } else + return ret; + } + break; + default: + break; + } + + return -EINVAL; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c new file mode 100644 index 000000000..e4ad1a6f6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c @@ -0,0 +1,239 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "chan.h" +#include "priv.h" +#include "head.h" +#include "ior.h" + +#include +#include + +#include + +void +tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + + nvkm_mask(device, 0x61657c + hoff, 0xffffffff, (aligned << 16) | pbn); + nvkm_mask(device, 0x616578 + hoff, 0x00003f3f, (slot_nr << 8) | slot); +} + +static int +tu102_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + + clksor |= sor->dp.bw << 18; + dpctrl |= ((1 << sor->dp.nr) - 1) << 16; + if (sor->dp.mst) + dpctrl |= 0x40000000; + if (sor->dp.ef) + dpctrl |= 0x00004000; + + nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor); + + /*XXX*/ + nvkm_msec(device, 40, NVKM_DELAY); + nvkm_mask(device, 0x612300 + soff, 0x00030000, 0x00010000); + nvkm_mask(device, 0x61c10c + loff, 0x00000003, 0x00000001); + + nvkm_mask(device, 0x61c10c + loff, 0x401f4000, dpctrl); + return 0; +} + +static const struct nvkm_ior_func_dp +tu102_sor_dp = { + .lanes = { 0, 1, 2, 3 }, + .links = tu102_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gm107_sor_dp_pattern, + .drive = gm200_sor_dp_drive, + .vcpi = tu102_sor_dp_vcpi, + .audio = gv100_sor_dp_audio, + .audio_sym = gv100_sor_dp_audio_sym, + .watermark = gv100_sor_dp_watermark, +}; + +static const struct nvkm_ior_func +tu102_sor = { + .route = { + .get = gm200_sor_route_get, + .set = gm200_sor_route_set, + }, + .state = gv100_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gv100_sor_hdmi_ctrl, + .scdc = gm200_sor_hdmi_scdc, + }, + .dp = &tu102_sor_dp, + .hda = &gv100_sor_hda, +}; + +static int +tu102_sor_new(struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + u32 hda = nvkm_rd32(device, 0x08a15c); + + return nvkm_ior_new_(&tu102_sor, disp, SOR, id, hda & BIT(id)); +} + +int +tu102_disp_init(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_head *head; + int i, j; + u32 tmp; + + /* Claim ownership of display. */ + if (nvkm_rd32(device, 0x6254e8) & 0x00000002) { + nvkm_mask(device, 0x6254e8, 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x6254e8) & 0x00000002)) + break; + ) < 0) + return -EBUSY; + } + + /* Lock pin capabilities. */ + tmp = 0x00000021; /*XXX*/ + nvkm_wr32(device, 0x640008, tmp); + + /* SOR capabilities. */ + for (i = 0; i < disp->sor.nr; i++) { + tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); + nvkm_mask(device, 0x640000, 0x00000100 << i, 0x00000100 << i); + nvkm_wr32(device, 0x640144 + (i * 0x08), tmp); + } + + /* Head capabilities. */ + list_for_each_entry(head, &disp->heads, head) { + const int id = head->id; + + /* RG. */ + tmp = nvkm_rd32(device, 0x616300 + (id * 0x800)); + nvkm_wr32(device, 0x640048 + (id * 0x020), tmp); + + /* POSTCOMP. */ + for (j = 0; j < 5 * 4; j += 4) { + tmp = nvkm_rd32(device, 0x616140 + (id * 0x800) + j); + nvkm_wr32(device, 0x640680 + (id * 0x20) + j, tmp); + } + } + + /* Window capabilities. */ + for (i = 0; i < disp->wndw.nr; i++) { + nvkm_mask(device, 0x640004, 1 << i, 1 << i); + for (j = 0; j < 6 * 4; j += 4) { + tmp = nvkm_rd32(device, 0x630100 + (i * 0x800) + j); + nvkm_mask(device, 0x640780 + (i * 0x20) + j, 0xffffffff, tmp); + } + nvkm_mask(device, 0x64000c, 0x00000100, 0x00000100); + } + + /* IHUB capabilities. */ + for (i = 0; i < 3; i++) { + tmp = nvkm_rd32(device, 0x62e000 + (i * 0x04)); + nvkm_wr32(device, 0x640010 + (i * 0x04), tmp); + } + + nvkm_mask(device, 0x610078, 0x00000001, 0x00000001); + + /* Setup instance memory. */ + switch (nvkm_memory_target(disp->inst->memory)) { + case NVKM_MEM_TARGET_VRAM: tmp = 0x00000001; break; + case NVKM_MEM_TARGET_NCOH: tmp = 0x00000002; break; + case NVKM_MEM_TARGET_HOST: tmp = 0x00000003; break; + default: + break; + } + nvkm_wr32(device, 0x610010, 0x00000008 | tmp); + nvkm_wr32(device, 0x610014, disp->inst->addr >> 16); + + /* CTRL_DISP: AWAKEN, ERROR, SUPERVISOR[1-3]. */ + nvkm_wr32(device, 0x611cf0, 0x00000187); /* MSK. */ + nvkm_wr32(device, 0x611db0, 0x00000187); /* EN. */ + + /* EXC_OTHER: CURSn, CORE. */ + nvkm_wr32(device, 0x611cec, disp->head.mask << 16 | + 0x00000001); /* MSK. */ + nvkm_wr32(device, 0x611dac, 0x00000000); /* EN. */ + + /* EXC_WINIM. */ + nvkm_wr32(device, 0x611ce8, disp->wndw.mask); /* MSK. */ + nvkm_wr32(device, 0x611da8, 0x00000000); /* EN. */ + + /* EXC_WIN. */ + nvkm_wr32(device, 0x611ce4, disp->wndw.mask); /* MSK. */ + nvkm_wr32(device, 0x611da4, 0x00000000); /* EN. */ + + /* HEAD_TIMING(n): VBLANK. */ + list_for_each_entry(head, &disp->heads, head) { + const u32 hoff = head->id * 4; + nvkm_wr32(device, 0x611cc0 + hoff, 0x00000004); /* MSK. */ + nvkm_wr32(device, 0x611d80 + hoff, 0x00000000); /* EN. */ + } + + /* OR. */ + nvkm_wr32(device, 0x611cf4, 0x00000000); /* MSK. */ + nvkm_wr32(device, 0x611db4, 0x00000000); /* EN. */ + return 0; +} + +static const struct nvkm_disp_func +tu102_disp = { + .oneinit = nv50_disp_oneinit, + .init = tu102_disp_init, + .fini = gv100_disp_fini, + .intr = gv100_disp_intr, + .super = gv100_disp_super, + .uevent = &gv100_disp_chan_uevent, + .wndw = { .cnt = gv100_disp_wndw_cnt }, + .head = { .cnt = gv100_head_cnt, .new = gv100_head_new }, + .sor = { .cnt = gv100_sor_cnt, .new = tu102_sor_new }, + .ramht_size = 0x2000, + .root = { 0, 0,TU102_DISP }, + .user = { + {{-1,-1,GV100_DISP_CAPS }, gv100_disp_caps_new }, + {{ 0, 0,TU102_DISP_CURSOR }, nvkm_disp_chan_new, &gv100_disp_curs }, + {{ 0, 0,TU102_DISP_WINDOW_IMM_CHANNEL_DMA}, nvkm_disp_wndw_new, &gv100_disp_wimm }, + {{ 0, 0,TU102_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gv100_disp_core }, + {{ 0, 0,TU102_DISP_WINDOW_CHANNEL_DMA }, nvkm_disp_wndw_new, &gv100_disp_wndw }, + {} + }, +}; + +int +tu102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return nvkm_disp_new_(&tu102_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c new file mode 100644 index 000000000..fd9f18144 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c @@ -0,0 +1,117 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#define nvkm_uconn(p) container_of((p), struct nvkm_conn, object) +#include "conn.h" + +#include + +#include + +static int +nvkm_uconn_mthd_hpd_status(struct nvkm_conn *conn, void *argv, u32 argc) +{ + struct nvkm_gpio *gpio = conn->disp->engine.subdev.device->gpio; + union nvif_conn_hpd_status_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + + args->v0.support = gpio && conn->info.hpd != DCB_GPIO_UNUSED; + args->v0.present = 0; + + if (args->v0.support) { + int ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, conn->info.hpd); + + if (WARN_ON(ret < 0)) { + args->v0.support = false; + return 0; + } + + args->v0.present = ret; + } + + return 0; +} + +static int +nvkm_uconn_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) +{ + struct nvkm_conn *conn = nvkm_uconn(object); + + switch (mthd) { + case NVIF_CONN_V0_HPD_STATUS: return nvkm_uconn_mthd_hpd_status(conn, argv, argc); + default: + break; + } + + return -EINVAL; +} + +static void * +nvkm_uconn_dtor(struct nvkm_object *object) +{ + struct nvkm_conn *conn = nvkm_uconn(object); + struct nvkm_disp *disp = conn->disp; + + spin_lock(&disp->client.lock); + conn->object.func = NULL; + spin_unlock(&disp->client.lock); + return NULL; +} + +static const struct nvkm_object_func +nvkm_uconn = { + .dtor = nvkm_uconn_dtor, + .mthd = nvkm_uconn_mthd, +}; + +int +nvkm_uconn_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = nvkm_udisp(oclass->parent); + struct nvkm_conn *cont, *conn = NULL; + union nvif_conn_args *args = argv; + int ret; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + + list_for_each_entry(cont, &disp->conns, head) { + if (cont->index == args->v0.id) { + conn = cont; + break; + } + } + + if (!conn) + return -EINVAL; + + ret = -EBUSY; + spin_lock(&disp->client.lock); + if (!conn->object.func) { + nvkm_object_ctor(&nvkm_uconn, oclass, &conn->object); + *pobject = &conn->object; + ret = 0; + } + spin_unlock(&disp->client.lock); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c new file mode 100644 index 000000000..0841e7ce0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c @@ -0,0 +1,115 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "conn.h" +#include "outp.h" + +#include +#include + +static int +nvkm_udisp_sclass(struct nvkm_object *object, int index, struct nvkm_oclass *sclass) +{ + struct nvkm_disp *disp = nvkm_udisp(object); + + if (index-- == 0) { + sclass->base = (struct nvkm_sclass) { 0, 0, NVIF_CLASS_CONN }; + sclass->ctor = nvkm_uconn_new; + return 0; + } + + if (index-- == 0) { + sclass->base = (struct nvkm_sclass) { 0, 0, NVIF_CLASS_OUTP }; + sclass->ctor = nvkm_uoutp_new; + return 0; + } + + if (disp->func->user[index].ctor) { + sclass->base = disp->func->user[index].base; + sclass->ctor = disp->func->user[index].ctor; + return 0; + } + + return -EINVAL; +} + +static int +nvkm_udisp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) +{ + struct nvkm_disp *disp = nvkm_udisp(object); + + if (disp->engine.subdev.device->card_type >= NV_50) + return nv50_disp_root_mthd_(object, mthd, argv, argc); + + return nv04_disp_mthd(object, mthd, argv, argc); +} + +static void * +nvkm_udisp_dtor(struct nvkm_object *object) +{ + struct nvkm_disp *disp = nvkm_udisp(object); + + spin_lock(&disp->client.lock); + if (object == &disp->client.object) + disp->client.object.func = NULL; + spin_unlock(&disp->client.lock); + return NULL; +} + +static const struct nvkm_object_func +nvkm_udisp = { + .dtor = nvkm_udisp_dtor, + .mthd = nvkm_udisp_mthd, + .ntfy = nvkm_disp_ntfy, + .sclass = nvkm_udisp_sclass, +}; + +int +nvkm_udisp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = nvkm_disp(oclass->engine); + struct nvkm_conn *conn; + struct nvkm_outp *outp; + union nvif_disp_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + + spin_lock(&disp->client.lock); + if (disp->client.object.func) { + spin_unlock(&disp->client.lock); + return -EBUSY; + } + nvkm_object_ctor(&nvkm_udisp, oclass, &disp->client.object); + *pobject = &disp->client.object; + spin_unlock(&disp->client.lock); + + args->v0.conn_mask = 0; + list_for_each_entry(conn, &disp->conns, head) + args->v0.conn_mask |= BIT(conn->index); + + args->v0.outp_mask = 0; + list_for_each_entry(outp, &disp->outps, head) + args->v0.outp_mask |= BIT(outp->index); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c new file mode 100644 index 000000000..abedb3e86 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c @@ -0,0 +1,129 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object) +#include "outp.h" +#include "ior.h" + +#include + +static int +nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_load_detect_args *args = argv; + int ret; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + + ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV, false); + if (ret == 0) { + if (outp->ior->func->sense) { + ret = outp->ior->func->sense(outp->ior, args->v0.data); + args->v0.load = ret < 0 ? 0 : ret; + } else { + ret = -EINVAL; + } + nvkm_outp_release(outp, NVKM_OUTP_PRIV); + } + + return ret; +} + +static int +nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc) +{ + switch (mthd) { + case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc); + default: + break; + } + + return 1; +} + +static int +nvkm_uoutp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) +{ + struct nvkm_outp *outp = nvkm_uoutp(object); + struct nvkm_disp *disp = outp->disp; + int ret; + + mutex_lock(&disp->super.mutex); + + ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc); + if (ret <= 0) + goto done; + +done: + mutex_unlock(&disp->super.mutex); + return ret; +} + +static void * +nvkm_uoutp_dtor(struct nvkm_object *object) +{ + struct nvkm_outp *outp = nvkm_uoutp(object); + struct nvkm_disp *disp = outp->disp; + + spin_lock(&disp->client.lock); + outp->object.func = NULL; + spin_unlock(&disp->client.lock); + return NULL; +} + +static const struct nvkm_object_func +nvkm_uoutp = { + .dtor = nvkm_uoutp_dtor, + .mthd = nvkm_uoutp_mthd, +}; + +int +nvkm_uoutp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = nvkm_udisp(oclass->parent); + struct nvkm_outp *outt, *outp = NULL; + union nvif_outp_args *args = argv; + int ret; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + + list_for_each_entry(outt, &disp->outps, head) { + if (outt->index == args->v0.id) { + outp = outt; + break; + } + } + + if (!outp) + return -EINVAL; + + ret = -EBUSY; + spin_lock(&disp->client.lock); + if (!outp->object.func) { + nvkm_object_ctor(&nvkm_uoutp, oclass, &outp->object); + *pobject = &outp->object; + ret = 0; + } + spin_unlock(&disp->client.lock); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c new file mode 100644 index 000000000..8bff95c63 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/vga.c @@ -0,0 +1,205 @@ +/* + * 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 + +u8 +nvkm_rdport(struct nvkm_device *device, int head, u16 port) +{ + if (device->card_type >= NV_50) + return nvkm_rd08(device, 0x601000 + port); + + if (port == 0x03c0 || port == 0x03c1 || /* AR */ + port == 0x03c2 || port == 0x03da || /* INP0 */ + port == 0x03d4 || port == 0x03d5) /* CR */ + return nvkm_rd08(device, 0x601000 + (head * 0x2000) + port); + + if (port == 0x03c2 || port == 0x03cc || /* MISC */ + port == 0x03c4 || port == 0x03c5 || /* SR */ + port == 0x03ce || port == 0x03cf) { /* GR */ + if (device->card_type < NV_40) + head = 0; /* CR44 selects head */ + return nvkm_rd08(device, 0x0c0000 + (head * 0x2000) + port); + } + + return 0x00; +} + +void +nvkm_wrport(struct nvkm_device *device, int head, u16 port, u8 data) +{ + if (device->card_type >= NV_50) + nvkm_wr08(device, 0x601000 + port, data); + else + if (port == 0x03c0 || port == 0x03c1 || /* AR */ + port == 0x03c2 || port == 0x03da || /* INP0 */ + port == 0x03d4 || port == 0x03d5) /* CR */ + nvkm_wr08(device, 0x601000 + (head * 0x2000) + port, data); + else + if (port == 0x03c2 || port == 0x03cc || /* MISC */ + port == 0x03c4 || port == 0x03c5 || /* SR */ + port == 0x03ce || port == 0x03cf) { /* GR */ + if (device->card_type < NV_40) + head = 0; /* CR44 selects head */ + nvkm_wr08(device, 0x0c0000 + (head * 0x2000) + port, data); + } +} + +u8 +nvkm_rdvgas(struct nvkm_device *device, int head, u8 index) +{ + nvkm_wrport(device, head, 0x03c4, index); + return nvkm_rdport(device, head, 0x03c5); +} + +void +nvkm_wrvgas(struct nvkm_device *device, int head, u8 index, u8 value) +{ + nvkm_wrport(device, head, 0x03c4, index); + nvkm_wrport(device, head, 0x03c5, value); +} + +u8 +nvkm_rdvgag(struct nvkm_device *device, int head, u8 index) +{ + nvkm_wrport(device, head, 0x03ce, index); + return nvkm_rdport(device, head, 0x03cf); +} + +void +nvkm_wrvgag(struct nvkm_device *device, int head, u8 index, u8 value) +{ + nvkm_wrport(device, head, 0x03ce, index); + nvkm_wrport(device, head, 0x03cf, value); +} + +u8 +nvkm_rdvgac(struct nvkm_device *device, int head, u8 index) +{ + nvkm_wrport(device, head, 0x03d4, index); + return nvkm_rdport(device, head, 0x03d5); +} + +void +nvkm_wrvgac(struct nvkm_device *device, int head, u8 index, u8 value) +{ + nvkm_wrport(device, head, 0x03d4, index); + nvkm_wrport(device, head, 0x03d5, value); +} + +u8 +nvkm_rdvgai(struct nvkm_device *device, int head, u16 port, u8 index) +{ + if (port == 0x03c4) return nvkm_rdvgas(device, head, index); + if (port == 0x03ce) return nvkm_rdvgag(device, head, index); + if (port == 0x03d4) return nvkm_rdvgac(device, head, index); + return 0x00; +} + +void +nvkm_wrvgai(struct nvkm_device *device, int head, u16 port, u8 index, u8 value) +{ + if (port == 0x03c4) nvkm_wrvgas(device, head, index, value); + else if (port == 0x03ce) nvkm_wrvgag(device, head, index, value); + else if (port == 0x03d4) nvkm_wrvgac(device, head, index, value); +} + +bool +nvkm_lockvgac(struct nvkm_device *device, bool lock) +{ + bool locked = !nvkm_rdvgac(device, 0, 0x1f); + u8 data = lock ? 0x99 : 0x57; + if (device->card_type < NV_50) + nvkm_wrvgac(device, 0, 0x1f, data); + else + nvkm_wrvgac(device, 0, 0x3f, data); + if (device->chipset == 0x11) { + if (!(nvkm_rd32(device, 0x001084) & 0x10000000)) + nvkm_wrvgac(device, 1, 0x1f, data); + } + return locked; +} + +/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied) + * it affects only the 8 bit vga io regs, which we access using mmio at + * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d* + * in general, the set value of cr44 does not matter: reg access works as + * expected and values can be set for the appropriate head by using a 0x2000 + * offset as required + * however: + * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and + * cr44 must be set to 0 or 3 for accessing values on the correct head + * through the common 0xc03c* addresses + * b) in tied mode (4) head B is programmed to the values set on head A, and + * access using the head B addresses can have strange results, ergo we leave + * tied mode in init once we know to what cr44 should be restored on exit + * + * the owner parameter is slightly abused: + * 0 and 1 are treated as head values and so the set value is (owner * 3) + * other values are treated as literal values to set + */ +u8 +nvkm_rdvgaowner(struct nvkm_device *device) +{ + if (device->card_type < NV_50) { + if (device->chipset == 0x11) { + u32 tied = nvkm_rd32(device, 0x001084) & 0x10000000; + if (tied == 0) { + u8 slA = nvkm_rdvgac(device, 0, 0x28) & 0x80; + u8 tvA = nvkm_rdvgac(device, 0, 0x33) & 0x01; + u8 slB = nvkm_rdvgac(device, 1, 0x28) & 0x80; + u8 tvB = nvkm_rdvgac(device, 1, 0x33) & 0x01; + if (slA && !tvA) return 0x00; + if (slB && !tvB) return 0x03; + if (slA) return 0x00; + if (slB) return 0x03; + return 0x00; + } + return 0x04; + } + + return nvkm_rdvgac(device, 0, 0x44); + } + + return 0x00; +} + +void +nvkm_wrvgaowner(struct nvkm_device *device, u8 select) +{ + if (device->card_type < NV_50) { + u8 owner = (select == 1) ? 3 : select; + if (device->chipset == 0x11) { + /* workaround hw lockup bug */ + nvkm_rdvgac(device, 0, 0x1f); + nvkm_rdvgac(device, 1, 0x1f); + } + + nvkm_wrvgac(device, 0, 0x44, owner); + + if (device->chipset == 0x11) { + nvkm_wrvgac(device, 0, 0x2e, owner); + nvkm_wrvgac(device, 0, 0x2e, owner); + } + } +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/dma/Kbuild new file mode 100644 index 000000000..a0e551b92 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/Kbuild @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/dma/base.o +nvkm-y += nvkm/engine/dma/nv04.o +nvkm-y += nvkm/engine/dma/nv50.o +nvkm-y += nvkm/engine/dma/gf100.o +nvkm-y += nvkm/engine/dma/gf119.o +nvkm-y += nvkm/engine/dma/gv100.o + +nvkm-y += nvkm/engine/dma/user.o +nvkm-y += nvkm/engine/dma/usernv04.o +nvkm-y += nvkm/engine/dma/usernv50.o +nvkm-y += nvkm/engine/dma/usergf100.o +nvkm-y += nvkm/engine/dma/usergf119.o +nvkm-y += nvkm/engine/dma/usergv100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c new file mode 100644 index 000000000..425cde35f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c @@ -0,0 +1,116 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +#include + +static int +nvkm_dma_oclass_new(struct nvkm_device *device, + const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_dma *dma = nvkm_dma(oclass->engine); + struct nvkm_dmaobj *dmaobj = NULL; + int ret; + + ret = dma->func->class_new(dma, oclass, data, size, &dmaobj); + if (dmaobj) + *pobject = &dmaobj->object; + return ret; +} + +static const struct nvkm_device_oclass +nvkm_dma_oclass_base = { + .ctor = nvkm_dma_oclass_new, +}; + +static int +nvkm_dma_oclass_fifo_new(const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + return nvkm_dma_oclass_new(oclass->engine->subdev.device, + oclass, data, size, pobject); +} + +static const struct nvkm_sclass +nvkm_dma_sclass[] = { + { 0, 0, NV_DMA_FROM_MEMORY, NULL, nvkm_dma_oclass_fifo_new }, + { 0, 0, NV_DMA_TO_MEMORY, NULL, nvkm_dma_oclass_fifo_new }, + { 0, 0, NV_DMA_IN_MEMORY, NULL, nvkm_dma_oclass_fifo_new }, +}; + +static int +nvkm_dma_oclass_base_get(struct nvkm_oclass *sclass, int index, + const struct nvkm_device_oclass **class) +{ + const int count = ARRAY_SIZE(nvkm_dma_sclass); + if (index < count) { + const struct nvkm_sclass *oclass = &nvkm_dma_sclass[index]; + sclass->base = oclass[0]; + sclass->engn = oclass; + *class = &nvkm_dma_oclass_base; + return index; + } + return count; +} + +static int +nvkm_dma_oclass_fifo_get(struct nvkm_oclass *oclass, int index) +{ + const int count = ARRAY_SIZE(nvkm_dma_sclass); + if (index < count) { + oclass->base = nvkm_dma_sclass[index]; + return index; + } + return count; +} + +static void * +nvkm_dma_dtor(struct nvkm_engine *engine) +{ + return nvkm_dma(engine); +} + +static const struct nvkm_engine_func +nvkm_dma = { + .dtor = nvkm_dma_dtor, + .base.sclass = nvkm_dma_oclass_base_get, + .fifo.sclass = nvkm_dma_oclass_fifo_get, +}; + +int +nvkm_dma_new_(const struct nvkm_dma_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_dma **pdma) +{ + struct nvkm_dma *dma; + + if (!(dma = *pdma = kzalloc(sizeof(*dma), GFP_KERNEL))) + return -ENOMEM; + dma->func = func; + + return nvkm_engine_ctor(&nvkm_dma, device, type, inst, true, &dma->engine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/gf100.c new file mode 100644 index 000000000..99a1e07fa --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/gf100.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "user.h" + +static const struct nvkm_dma_func +gf100_dma = { + .class_new = gf100_dmaobj_new, +}; + +int +gf100_dma_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_dma **pdma) +{ + return nvkm_dma_new_(&gf100_dma, device, type, inst, pdma); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/gf119.c new file mode 100644 index 000000000..fd1d1fc22 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/gf119.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "user.h" + +static const struct nvkm_dma_func +gf119_dma = { + .class_new = gf119_dmaobj_new, +}; + +int +gf119_dma_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_dma **pdma) +{ + return nvkm_dma_new_(&gf119_dma, device, type, inst, pdma); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/gv100.c new file mode 100644 index 000000000..a5af0df30 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/gv100.c @@ -0,0 +1,35 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "user.h" + +static const struct nvkm_dma_func +gv100_dma = { + .class_new = gv100_dmaobj_new, +}; + +int +gv100_dma_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_dma **pdma) +{ + return nvkm_dma_new_(&gv100_dma, device, type, inst, pdma); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/nv04.c new file mode 100644 index 000000000..ea5a889f6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/nv04.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "user.h" + +static const struct nvkm_dma_func +nv04_dma = { + .class_new = nv04_dmaobj_new, +}; + +int +nv04_dma_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_dma **pdma) +{ + return nvkm_dma_new_(&nv04_dma, device, type, inst, pdma); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/nv50.c new file mode 100644 index 000000000..6e8f79660 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/nv50.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "user.h" + +static const struct nvkm_dma_func +nv50_dma = { + .class_new = nv50_dmaobj_new, +}; + +int +nv50_dma_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_dma **pdma) +{ + return nvkm_dma_new_(&nv50_dma, device, type, inst, pdma); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/dma/priv.h new file mode 100644 index 000000000..d403bedb4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/priv.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DMA_PRIV_H__ +#define __NVKM_DMA_PRIV_H__ +#define nvkm_dma(p) container_of((p), struct nvkm_dma, engine) +#include + +struct nvkm_dmaobj_func { + int (*bind)(struct nvkm_dmaobj *, struct nvkm_gpuobj *, int align, + struct nvkm_gpuobj **); +}; + +int nvkm_dma_new_(const struct nvkm_dma_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_dma **); + +struct nvkm_dma_func { + int (*class_new)(struct nvkm_dma *, const struct nvkm_oclass *, + void *data, u32 size, struct nvkm_dmaobj **); +}; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c new file mode 100644 index 000000000..797131ed7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c @@ -0,0 +1,138 @@ +/* + * 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 "user.h" + +#include +#include +#include + +#include +#include + +static const struct nvkm_object_func nvkm_dmaobj_func; +struct nvkm_dmaobj * +nvkm_dmaobj_search(struct nvkm_client *client, u64 handle) +{ + struct nvkm_object *object; + + object = nvkm_object_search(client, handle, &nvkm_dmaobj_func); + if (IS_ERR(object)) + return (void *)object; + + return nvkm_dmaobj(object); +} + +static int +nvkm_dmaobj_bind(struct nvkm_object *base, struct nvkm_gpuobj *gpuobj, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_dmaobj *dmaobj = nvkm_dmaobj(base); + return dmaobj->func->bind(dmaobj, gpuobj, align, pgpuobj); +} + +static void * +nvkm_dmaobj_dtor(struct nvkm_object *base) +{ + return nvkm_dmaobj(base); +} + +static const struct nvkm_object_func +nvkm_dmaobj_func = { + .dtor = nvkm_dmaobj_dtor, + .bind = nvkm_dmaobj_bind, +}; + +int +nvkm_dmaobj_ctor(const struct nvkm_dmaobj_func *func, struct nvkm_dma *dma, + const struct nvkm_oclass *oclass, void **pdata, u32 *psize, + struct nvkm_dmaobj *dmaobj) +{ + union { + struct nv_dma_v0 v0; + } *args = *pdata; + struct nvkm_object *parent = oclass->parent; + void *data = *pdata; + u32 size = *psize; + int ret = -ENOSYS; + + nvkm_object_ctor(&nvkm_dmaobj_func, oclass, &dmaobj->object); + dmaobj->func = func; + dmaobj->dma = dma; + + nvif_ioctl(parent, "create dma size %d\n", *psize); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(parent, "create dma vers %d target %d access %d " + "start %016llx limit %016llx\n", + args->v0.version, args->v0.target, args->v0.access, + args->v0.start, args->v0.limit); + dmaobj->target = args->v0.target; + dmaobj->access = args->v0.access; + dmaobj->start = args->v0.start; + dmaobj->limit = args->v0.limit; + } else + return ret; + + *pdata = data; + *psize = size; + + if (dmaobj->start > dmaobj->limit) + return -EINVAL; + + switch (dmaobj->target) { + case NV_DMA_V0_TARGET_VM: + dmaobj->target = NV_MEM_TARGET_VM; + break; + case NV_DMA_V0_TARGET_VRAM: + dmaobj->target = NV_MEM_TARGET_VRAM; + break; + case NV_DMA_V0_TARGET_PCI: + dmaobj->target = NV_MEM_TARGET_PCI; + break; + case NV_DMA_V0_TARGET_PCI_US: + case NV_DMA_V0_TARGET_AGP: + dmaobj->target = NV_MEM_TARGET_PCI_NOSNOOP; + break; + default: + return -EINVAL; + } + + switch (dmaobj->access) { + case NV_DMA_V0_ACCESS_VM: + dmaobj->access = NV_MEM_ACCESS_VM; + break; + case NV_DMA_V0_ACCESS_RD: + dmaobj->access = NV_MEM_ACCESS_RO; + break; + case NV_DMA_V0_ACCESS_WR: + dmaobj->access = NV_MEM_ACCESS_WO; + break; + case NV_DMA_V0_ACCESS_RDWR: + dmaobj->access = NV_MEM_ACCESS_RW; + break; + default: + return -EINVAL; + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.h b/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.h new file mode 100644 index 000000000..9c72ee214 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DMA_USER_H__ +#define __NVKM_DMA_USER_H__ +#define nvkm_dmaobj(p) container_of((p), struct nvkm_dmaobj, object) +#include "priv.h" + +int nvkm_dmaobj_ctor(const struct nvkm_dmaobj_func *, struct nvkm_dma *, + const struct nvkm_oclass *, void **data, u32 *size, + struct nvkm_dmaobj *); + +int nv04_dmaobj_new(struct nvkm_dma *, const struct nvkm_oclass *, void *, u32, + struct nvkm_dmaobj **); +int nv50_dmaobj_new(struct nvkm_dma *, const struct nvkm_oclass *, void *, u32, + struct nvkm_dmaobj **); +int gf100_dmaobj_new(struct nvkm_dma *, const struct nvkm_oclass *, void *, u32, + struct nvkm_dmaobj **); +int gf119_dmaobj_new(struct nvkm_dma *, const struct nvkm_oclass *, void *, u32, + struct nvkm_dmaobj **); +int gv100_dmaobj_new(struct nvkm_dma *, const struct nvkm_oclass *, void *, u32, + struct nvkm_dmaobj **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf100.c new file mode 100644 index 000000000..ef7ac3601 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf100.c @@ -0,0 +1,150 @@ +/* + * 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 + */ +#define gf100_dmaobj(p) container_of((p), struct gf100_dmaobj, base) +#include "user.h" + +#include +#include +#include + +#include +#include + +struct gf100_dmaobj { + struct nvkm_dmaobj base; + u32 flags0; + u32 flags5; +}; + +static int +gf100_dmaobj_bind(struct nvkm_dmaobj *base, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct gf100_dmaobj *dmaobj = gf100_dmaobj(base); + struct nvkm_device *device = dmaobj->base.dma->engine.subdev.device; + int ret; + + ret = nvkm_gpuobj_new(device, 24, align, false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, dmaobj->flags0); + nvkm_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->base.limit)); + nvkm_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->base.start)); + nvkm_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->base.limit) << 24 | + upper_32_bits(dmaobj->base.start)); + nvkm_wo32(*pgpuobj, 0x10, 0x00000000); + nvkm_wo32(*pgpuobj, 0x14, dmaobj->flags5); + nvkm_done(*pgpuobj); + } + + return ret; +} + +static const struct nvkm_dmaobj_func +gf100_dmaobj_func = { + .bind = gf100_dmaobj_bind, +}; + +int +gf100_dmaobj_new(struct nvkm_dma *dma, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_dmaobj **pdmaobj) +{ + union { + struct gf100_dma_v0 v0; + } *args; + struct nvkm_object *parent = oclass->parent; + struct gf100_dmaobj *dmaobj; + u32 kind, user, unkn; + int ret; + + if (!(dmaobj = kzalloc(sizeof(*dmaobj), GFP_KERNEL))) + return -ENOMEM; + *pdmaobj = &dmaobj->base; + + ret = nvkm_dmaobj_ctor(&gf100_dmaobj_func, dma, oclass, + &data, &size, &dmaobj->base); + if (ret) + return ret; + + ret = -ENOSYS; + args = data; + + nvif_ioctl(parent, "create gf100 dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, + "create gf100 dma vers %d priv %d kind %02x\n", + args->v0.version, args->v0.priv, args->v0.kind); + kind = args->v0.kind; + user = args->v0.priv; + unkn = 0; + } else + if (size == 0) { + if (dmaobj->base.target != NV_MEM_TARGET_VM) { + kind = GF100_DMA_V0_KIND_PITCH; + user = GF100_DMA_V0_PRIV_US; + unkn = 2; + } else { + kind = GF100_DMA_V0_KIND_VM; + user = GF100_DMA_V0_PRIV_VM; + unkn = 0; + } + } else + return ret; + + if (user > 2) + return -EINVAL; + dmaobj->flags0 |= (kind << 22) | (user << 20) | oclass->base.oclass; + dmaobj->flags5 |= (unkn << 16); + + switch (dmaobj->base.target) { + case NV_MEM_TARGET_VM: + dmaobj->flags0 |= 0x00000000; + break; + case NV_MEM_TARGET_VRAM: + dmaobj->flags0 |= 0x00010000; + break; + case NV_MEM_TARGET_PCI: + dmaobj->flags0 |= 0x00020000; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + dmaobj->flags0 |= 0x00030000; + break; + default: + return -EINVAL; + } + + switch (dmaobj->base.access) { + case NV_MEM_ACCESS_VM: + break; + case NV_MEM_ACCESS_RO: + dmaobj->flags0 |= 0x00040000; + break; + case NV_MEM_ACCESS_WO: + case NV_MEM_ACCESS_RW: + dmaobj->flags0 |= 0x00080000; + break; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf119.c new file mode 100644 index 000000000..c068cee34 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergf119.c @@ -0,0 +1,132 @@ +/* + * 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 + */ +#define gf119_dmaobj(p) container_of((p), struct gf119_dmaobj, base) +#include "user.h" + +#include +#include +#include + +#include +#include + +struct gf119_dmaobj { + struct nvkm_dmaobj base; + u32 flags0; +}; + +static int +gf119_dmaobj_bind(struct nvkm_dmaobj *base, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct gf119_dmaobj *dmaobj = gf119_dmaobj(base); + struct nvkm_device *device = dmaobj->base.dma->engine.subdev.device; + int ret; + + ret = nvkm_gpuobj_new(device, 24, align, false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, dmaobj->flags0); + nvkm_wo32(*pgpuobj, 0x04, dmaobj->base.start >> 8); + nvkm_wo32(*pgpuobj, 0x08, dmaobj->base.limit >> 8); + nvkm_wo32(*pgpuobj, 0x0c, 0x00000000); + nvkm_wo32(*pgpuobj, 0x10, 0x00000000); + nvkm_wo32(*pgpuobj, 0x14, 0x00000000); + nvkm_done(*pgpuobj); + } + + return ret; +} + +static const struct nvkm_dmaobj_func +gf119_dmaobj_func = { + .bind = gf119_dmaobj_bind, +}; + +int +gf119_dmaobj_new(struct nvkm_dma *dma, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_dmaobj **pdmaobj) +{ + union { + struct gf119_dma_v0 v0; + } *args; + struct nvkm_object *parent = oclass->parent; + struct gf119_dmaobj *dmaobj; + u32 kind, page; + int ret; + + if (!(dmaobj = kzalloc(sizeof(*dmaobj), GFP_KERNEL))) + return -ENOMEM; + *pdmaobj = &dmaobj->base; + + ret = nvkm_dmaobj_ctor(&gf119_dmaobj_func, dma, oclass, + &data, &size, &dmaobj->base); + if (ret) + return ret; + + ret = -ENOSYS; + args = data; + + nvif_ioctl(parent, "create gf119 dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, + "create gf100 dma vers %d page %d kind %02x\n", + args->v0.version, args->v0.page, args->v0.kind); + kind = args->v0.kind; + page = args->v0.page; + } else + if (size == 0) { + if (dmaobj->base.target != NV_MEM_TARGET_VM) { + kind = GF119_DMA_V0_KIND_PITCH; + page = GF119_DMA_V0_PAGE_SP; + } else { + kind = GF119_DMA_V0_KIND_VM; + page = GF119_DMA_V0_PAGE_LP; + } + } else + return ret; + + if (page > 1) + return -EINVAL; + dmaobj->flags0 = (kind << 20) | (page << 6); + + switch (dmaobj->base.target) { + case NV_MEM_TARGET_VRAM: + dmaobj->flags0 |= 0x00000009; + break; + case NV_MEM_TARGET_VM: + case NV_MEM_TARGET_PCI: + case NV_MEM_TARGET_PCI_NOSNOOP: + /* XXX: don't currently know how to construct a real one + * of these. we only use them to represent pushbufs + * on these chipsets, and the classes that use them + * deal with the target themselves. + */ + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergv100.c new file mode 100644 index 000000000..39eba9fc8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usergv100.c @@ -0,0 +1,119 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#define gv100_dmaobj(p) container_of((p), struct gv100_dmaobj, base) +#include "user.h" + +#include +#include +#include + +#include +#include + +struct gv100_dmaobj { + struct nvkm_dmaobj base; + u32 flags0; +}; + +static int +gv100_dmaobj_bind(struct nvkm_dmaobj *base, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct gv100_dmaobj *dmaobj = gv100_dmaobj(base); + struct nvkm_device *device = dmaobj->base.dma->engine.subdev.device; + u64 start = dmaobj->base.start >> 8; + u64 limit = dmaobj->base.limit >> 8; + int ret; + + ret = nvkm_gpuobj_new(device, 24, align, false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, dmaobj->flags0); + nvkm_wo32(*pgpuobj, 0x04, lower_32_bits(start)); + nvkm_wo32(*pgpuobj, 0x08, upper_32_bits(start)); + nvkm_wo32(*pgpuobj, 0x0c, lower_32_bits(limit)); + nvkm_wo32(*pgpuobj, 0x10, upper_32_bits(limit)); + nvkm_done(*pgpuobj); + } + + return ret; +} + +static const struct nvkm_dmaobj_func +gv100_dmaobj_func = { + .bind = gv100_dmaobj_bind, +}; + +int +gv100_dmaobj_new(struct nvkm_dma *dma, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_dmaobj **pdmaobj) +{ + union { + struct gf119_dma_v0 v0; + } *args; + struct nvkm_object *parent = oclass->parent; + struct gv100_dmaobj *dmaobj; + u32 kind, page; + int ret; + + if (!(dmaobj = kzalloc(sizeof(*dmaobj), GFP_KERNEL))) + return -ENOMEM; + *pdmaobj = &dmaobj->base; + + ret = nvkm_dmaobj_ctor(&gv100_dmaobj_func, dma, oclass, + &data, &size, &dmaobj->base); + if (ret) + return ret; + + ret = -ENOSYS; + args = data; + + nvif_ioctl(parent, "create gv100 dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, + "create gv100 dma vers %d page %d kind %02x\n", + args->v0.version, args->v0.page, args->v0.kind); + kind = args->v0.kind != 0; + page = args->v0.page != 0; + } else + if (size == 0) { + kind = 0; + page = GF119_DMA_V0_PAGE_SP; + } else + return ret; + + if (kind) + dmaobj->flags0 |= 0x00100000; + if (page) + dmaobj->flags0 |= 0x00000040; + dmaobj->flags0 |= 0x00000004; /* rw */ + + switch (dmaobj->base.target) { + case NV_MEM_TARGET_VRAM : dmaobj->flags0 |= 0x00000001; break; + case NV_MEM_TARGET_PCI : dmaobj->flags0 |= 0x00000002; break; + case NV_MEM_TARGET_PCI_NOSNOOP: dmaobj->flags0 |= 0x00000003; break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv04.c new file mode 100644 index 000000000..5159d5df2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv04.c @@ -0,0 +1,134 @@ +/* + * 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 + */ +#define nv04_dmaobj(p) container_of((p), struct nv04_dmaobj, base) +#include "user.h" + +#include +#include +#include + +#include + +struct nv04_dmaobj { + struct nvkm_dmaobj base; + bool clone; + u32 flags0; + u32 flags2; +}; + +static int +nv04_dmaobj_bind(struct nvkm_dmaobj *base, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct nv04_dmaobj *dmaobj = nv04_dmaobj(base); + struct nvkm_device *device = dmaobj->base.dma->engine.subdev.device; + u64 offset = dmaobj->base.start & 0xfffff000; + u64 adjust = dmaobj->base.start & 0x00000fff; + u32 length = dmaobj->base.limit - dmaobj->base.start; + int ret; + + if (dmaobj->clone) { + struct nvkm_memory *pgt = + device->mmu->vmm->pd->pt[0]->memory; + if (!dmaobj->base.start) + return nvkm_gpuobj_wrap(pgt, pgpuobj); + nvkm_kmap(pgt); + offset = nvkm_ro32(pgt, 8 + (offset >> 10)); + offset &= 0xfffff000; + nvkm_done(pgt); + } + + ret = nvkm_gpuobj_new(device, 16, align, false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, dmaobj->flags0 | (adjust << 20)); + nvkm_wo32(*pgpuobj, 0x04, length); + nvkm_wo32(*pgpuobj, 0x08, dmaobj->flags2 | offset); + nvkm_wo32(*pgpuobj, 0x0c, dmaobj->flags2 | offset); + nvkm_done(*pgpuobj); + } + + return ret; +} + +static const struct nvkm_dmaobj_func +nv04_dmaobj_func = { + .bind = nv04_dmaobj_bind, +}; + +int +nv04_dmaobj_new(struct nvkm_dma *dma, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_dmaobj **pdmaobj) +{ + struct nvkm_device *device = dma->engine.subdev.device; + struct nv04_dmaobj *dmaobj; + int ret; + + if (!(dmaobj = kzalloc(sizeof(*dmaobj), GFP_KERNEL))) + return -ENOMEM; + *pdmaobj = &dmaobj->base; + + ret = nvkm_dmaobj_ctor(&nv04_dmaobj_func, dma, oclass, + &data, &size, &dmaobj->base); + if (ret) + return ret; + + if (dmaobj->base.target == NV_MEM_TARGET_VM) { + if (device->mmu->func == &nv04_mmu) + dmaobj->clone = true; + dmaobj->base.target = NV_MEM_TARGET_PCI; + dmaobj->base.access = NV_MEM_ACCESS_RW; + } + + dmaobj->flags0 = oclass->base.oclass; + switch (dmaobj->base.target) { + case NV_MEM_TARGET_VRAM: + dmaobj->flags0 |= 0x00003000; + break; + case NV_MEM_TARGET_PCI: + dmaobj->flags0 |= 0x00023000; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + dmaobj->flags0 |= 0x00033000; + break; + default: + return -EINVAL; + } + + switch (dmaobj->base.access) { + case NV_MEM_ACCESS_RO: + dmaobj->flags0 |= 0x00004000; + break; + case NV_MEM_ACCESS_WO: + dmaobj->flags0 |= 0x00008000; + fallthrough; + case NV_MEM_ACCESS_RW: + dmaobj->flags2 |= 0x00000002; + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv50.c new file mode 100644 index 000000000..6a85b5dea --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/usernv50.c @@ -0,0 +1,157 @@ +/* + * 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 + */ +#define nv50_dmaobj(p) container_of((p), struct nv50_dmaobj, base) +#include "user.h" + +#include +#include +#include + +#include +#include + +struct nv50_dmaobj { + struct nvkm_dmaobj base; + u32 flags0; + u32 flags5; +}; + +static int +nv50_dmaobj_bind(struct nvkm_dmaobj *base, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct nv50_dmaobj *dmaobj = nv50_dmaobj(base); + struct nvkm_device *device = dmaobj->base.dma->engine.subdev.device; + int ret; + + ret = nvkm_gpuobj_new(device, 24, align, false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, dmaobj->flags0); + nvkm_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->base.limit)); + nvkm_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->base.start)); + nvkm_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->base.limit) << 24 | + upper_32_bits(dmaobj->base.start)); + nvkm_wo32(*pgpuobj, 0x10, 0x00000000); + nvkm_wo32(*pgpuobj, 0x14, dmaobj->flags5); + nvkm_done(*pgpuobj); + } + + return ret; +} + +static const struct nvkm_dmaobj_func +nv50_dmaobj_func = { + .bind = nv50_dmaobj_bind, +}; + +int +nv50_dmaobj_new(struct nvkm_dma *dma, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_dmaobj **pdmaobj) +{ + union { + struct nv50_dma_v0 v0; + } *args; + struct nvkm_object *parent = oclass->parent; + struct nv50_dmaobj *dmaobj; + u32 user, part, comp, kind; + int ret; + + if (!(dmaobj = kzalloc(sizeof(*dmaobj), GFP_KERNEL))) + return -ENOMEM; + *pdmaobj = &dmaobj->base; + + ret = nvkm_dmaobj_ctor(&nv50_dmaobj_func, dma, oclass, + &data, &size, &dmaobj->base); + if (ret) + return ret; + + ret = -ENOSYS; + args = data; + + nvif_ioctl(parent, "create nv50 dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create nv50 dma vers %d priv %d part %d " + "comp %d kind %02x\n", args->v0.version, + args->v0.priv, args->v0.part, args->v0.comp, + args->v0.kind); + user = args->v0.priv; + part = args->v0.part; + comp = args->v0.comp; + kind = args->v0.kind; + } else + if (size == 0) { + if (dmaobj->base.target != NV_MEM_TARGET_VM) { + user = NV50_DMA_V0_PRIV_US; + part = NV50_DMA_V0_PART_256; + comp = NV50_DMA_V0_COMP_NONE; + kind = NV50_DMA_V0_KIND_PITCH; + } else { + user = NV50_DMA_V0_PRIV_VM; + part = NV50_DMA_V0_PART_VM; + comp = NV50_DMA_V0_COMP_VM; + kind = NV50_DMA_V0_KIND_VM; + } + } else + return ret; + + if (user > 2 || part > 2 || comp > 3 || kind > 0x7f) + return -EINVAL; + dmaobj->flags0 = (comp << 29) | (kind << 22) | (user << 20) | + oclass->base.oclass; + dmaobj->flags5 = (part << 16); + + switch (dmaobj->base.target) { + case NV_MEM_TARGET_VM: + dmaobj->flags0 |= 0x00000000; + break; + case NV_MEM_TARGET_VRAM: + dmaobj->flags0 |= 0x00010000; + break; + case NV_MEM_TARGET_PCI: + dmaobj->flags0 |= 0x00020000; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + dmaobj->flags0 |= 0x00030000; + break; + default: + return -EINVAL; + } + + switch (dmaobj->base.access) { + case NV_MEM_ACCESS_VM: + break; + case NV_MEM_ACCESS_RO: + dmaobj->flags0 |= 0x00040000; + break; + case NV_MEM_ACCESS_WO: + case NV_MEM_ACCESS_RW: + dmaobj->flags0 |= 0x00080000; + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/falcon.c b/drivers/gpu/drm/nouveau/nvkm/engine/falcon.c new file mode 100644 index 000000000..43b7dec45 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/falcon.c @@ -0,0 +1,355 @@ +/* + * 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. + */ +#include + +#include +#include +#include +#include + +static int +nvkm_falcon_oclass_get(struct nvkm_oclass *oclass, int index) +{ + struct nvkm_falcon *falcon = nvkm_falcon(oclass->engine); + int c = 0; + + while (falcon->func->sclass[c].oclass) { + if (c++ == index) { + oclass->base = falcon->func->sclass[index]; + return index; + } + } + + return c; +} + +static int +nvkm_falcon_cclass_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + return nvkm_gpuobj_new(object->engine->subdev.device, 256, + align, true, parent, pgpuobj); +} + +static const struct nvkm_object_func +nvkm_falcon_cclass = { + .bind = nvkm_falcon_cclass_bind, +}; + +static void +nvkm_falcon_intr(struct nvkm_engine *engine) +{ + struct nvkm_falcon *falcon = nvkm_falcon(engine); + struct nvkm_subdev *subdev = &falcon->engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 base = falcon->addr; + u32 dest = nvkm_rd32(device, base + 0x01c); + u32 intr = nvkm_rd32(device, base + 0x008) & dest & ~(dest >> 16); + u32 inst = nvkm_rd32(device, base + 0x050) & 0x3fffffff; + struct nvkm_fifo_chan *chan; + unsigned long flags; + + chan = nvkm_fifo_chan_inst(device->fifo, (u64)inst << 12, &flags); + + if (intr & 0x00000040) { + if (falcon->func->intr) { + falcon->func->intr(falcon, chan); + nvkm_wr32(device, base + 0x004, 0x00000040); + intr &= ~0x00000040; + } + } + + if (intr & 0x00000010) { + nvkm_debug(subdev, "ucode halted\n"); + nvkm_wr32(device, base + 0x004, 0x00000010); + intr &= ~0x00000010; + } + + if (intr) { + nvkm_error(subdev, "intr %08x\n", intr); + nvkm_wr32(device, base + 0x004, intr); + } + + nvkm_fifo_chan_put(device->fifo, flags, &chan); +} + +static int +nvkm_falcon_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_falcon *falcon = nvkm_falcon(engine); + struct nvkm_device *device = falcon->engine.subdev.device; + const u32 base = falcon->addr; + + if (!suspend) { + nvkm_memory_unref(&falcon->core); + if (falcon->external) { + vfree(falcon->data.data); + vfree(falcon->code.data); + falcon->code.data = NULL; + } + } + + if (nvkm_mc_enabled(device, engine->subdev.type, engine->subdev.inst)) { + nvkm_mask(device, base + 0x048, 0x00000003, 0x00000000); + nvkm_wr32(device, base + 0x014, 0xffffffff); + } + return 0; +} + +static void * +vmemdup(const void *src, size_t len) +{ + void *p = vmalloc(len); + + if (p) + memcpy(p, src, len); + return p; +} + +static int +nvkm_falcon_oneinit(struct nvkm_engine *engine) +{ + struct nvkm_falcon *falcon = nvkm_falcon(engine); + struct nvkm_subdev *subdev = &falcon->engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 base = falcon->addr; + u32 caps; + + /* determine falcon capabilities */ + if (device->chipset < 0xa3 || + device->chipset == 0xaa || device->chipset == 0xac) { + falcon->version = 0; + falcon->secret = (falcon->addr == 0x087000) ? 1 : 0; + } else { + caps = nvkm_rd32(device, base + 0x12c); + falcon->version = (caps & 0x0000000f); + falcon->secret = (caps & 0x00000030) >> 4; + } + + caps = nvkm_rd32(device, base + 0x108); + falcon->code.limit = (caps & 0x000001ff) << 8; + falcon->data.limit = (caps & 0x0003fe00) >> 1; + + nvkm_debug(subdev, "falcon version: %d\n", falcon->version); + nvkm_debug(subdev, "secret level: %d\n", falcon->secret); + nvkm_debug(subdev, "code limit: %d\n", falcon->code.limit); + nvkm_debug(subdev, "data limit: %d\n", falcon->data.limit); + return 0; +} + +static int +nvkm_falcon_init(struct nvkm_engine *engine) +{ + struct nvkm_falcon *falcon = nvkm_falcon(engine); + struct nvkm_subdev *subdev = &falcon->engine.subdev; + struct nvkm_device *device = subdev->device; + const struct firmware *fw; + char name[32] = "internal"; + const u32 base = falcon->addr; + int ret, i; + + /* wait for 'uc halted' to be signalled before continuing */ + if (falcon->secret && falcon->version < 4) { + if (!falcon->version) { + nvkm_msec(device, 2000, + if (nvkm_rd32(device, base + 0x008) & 0x00000010) + break; + ); + } else { + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, base + 0x180) & 0x80000000)) + break; + ); + } + nvkm_wr32(device, base + 0x004, 0x00000010); + } + + /* disable all interrupts */ + nvkm_wr32(device, base + 0x014, 0xffffffff); + + /* no default ucode provided by the engine implementation, try and + * locate a "self-bootstrapping" firmware image for the engine + */ + if (!falcon->code.data) { + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, device->dev); + if (ret == 0) { + falcon->code.data = vmemdup(fw->data, fw->size); + falcon->code.size = fw->size; + falcon->data.data = NULL; + falcon->data.size = 0; + release_firmware(fw); + } + + falcon->external = true; + } + + /* next step is to try and load "static code/data segment" firmware + * images for the engine + */ + if (!falcon->code.data) { + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, device->dev); + if (ret) { + nvkm_error(subdev, "unable to load firmware data\n"); + return -ENODEV; + } + + falcon->data.data = vmemdup(fw->data, fw->size); + falcon->data.size = fw->size; + release_firmware(fw); + if (!falcon->data.data) + return -ENOMEM; + + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, device->dev); + if (ret) { + nvkm_error(subdev, "unable to load firmware code\n"); + return -ENODEV; + } + + falcon->code.data = vmemdup(fw->data, fw->size); + falcon->code.size = fw->size; + release_firmware(fw); + if (!falcon->code.data) + return -ENOMEM; + } + + nvkm_debug(subdev, "firmware: %s (%s)\n", name, falcon->data.data ? + "static code/data segments" : "self-bootstrapping"); + + /* ensure any "self-bootstrapping" firmware image is in vram */ + if (!falcon->data.data && !falcon->core) { + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, + falcon->code.size, 256, false, + &falcon->core); + if (ret) { + nvkm_error(subdev, "core allocation failed, %d\n", ret); + return ret; + } + + nvkm_kmap(falcon->core); + for (i = 0; i < falcon->code.size; i += 4) + nvkm_wo32(falcon->core, i, falcon->code.data[i / 4]); + nvkm_done(falcon->core); + } + + /* upload firmware bootloader (or the full code segments) */ + if (falcon->core) { + u64 addr = nvkm_memory_addr(falcon->core); + if (device->card_type < NV_C0) + nvkm_wr32(device, base + 0x618, 0x04000000); + else + nvkm_wr32(device, base + 0x618, 0x00000114); + nvkm_wr32(device, base + 0x11c, 0); + nvkm_wr32(device, base + 0x110, addr >> 8); + nvkm_wr32(device, base + 0x114, 0); + nvkm_wr32(device, base + 0x118, 0x00006610); + } else { + if (falcon->code.size > falcon->code.limit || + falcon->data.size > falcon->data.limit) { + nvkm_error(subdev, "ucode exceeds falcon limit(s)\n"); + return -EINVAL; + } + + if (falcon->version < 3) { + nvkm_wr32(device, base + 0xff8, 0x00100000); + for (i = 0; i < falcon->code.size / 4; i++) + nvkm_wr32(device, base + 0xff4, falcon->code.data[i]); + } else { + nvkm_wr32(device, base + 0x180, 0x01000000); + for (i = 0; i < falcon->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nvkm_wr32(device, base + 0x188, i >> 6); + nvkm_wr32(device, base + 0x184, falcon->code.data[i]); + } + } + } + + /* upload data segment (if necessary), zeroing the remainder */ + if (falcon->version < 3) { + nvkm_wr32(device, base + 0xff8, 0x00000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nvkm_wr32(device, base + 0xff4, falcon->data.data[i]); + for (; i < falcon->data.limit; i += 4) + nvkm_wr32(device, base + 0xff4, 0x00000000); + } else { + nvkm_wr32(device, base + 0x1c0, 0x01000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nvkm_wr32(device, base + 0x1c4, falcon->data.data[i]); + for (; i < falcon->data.limit / 4; i++) + nvkm_wr32(device, base + 0x1c4, 0x00000000); + } + + /* start it running */ + nvkm_wr32(device, base + 0x10c, 0x00000001); /* BLOCK_ON_FIFO */ + nvkm_wr32(device, base + 0x104, 0x00000000); /* ENTRY */ + nvkm_wr32(device, base + 0x100, 0x00000002); /* TRIGGER */ + nvkm_wr32(device, base + 0x048, 0x00000003); /* FIFO | CHSW */ + + if (falcon->func->init) + falcon->func->init(falcon); + return 0; +} + +static void * +nvkm_falcon_dtor(struct nvkm_engine *engine) +{ + return nvkm_falcon(engine); +} + +static const struct nvkm_engine_func +nvkm_falcon = { + .dtor = nvkm_falcon_dtor, + .oneinit = nvkm_falcon_oneinit, + .init = nvkm_falcon_init, + .fini = nvkm_falcon_fini, + .intr = nvkm_falcon_intr, + .fifo.sclass = nvkm_falcon_oclass_get, + .cclass = &nvkm_falcon_cclass, +}; + +int +nvkm_falcon_new_(const struct nvkm_falcon_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool enable, u32 addr, + struct nvkm_engine **pengine) +{ + struct nvkm_falcon *falcon; + + if (!(falcon = kzalloc(sizeof(*falcon), GFP_KERNEL))) + return -ENOMEM; + falcon->func = func; + falcon->addr = addr; + falcon->code.data = func->code.data; + falcon->code.size = func->code.size; + falcon->data.data = func->data.data; + falcon->data.size = func->data.size; + *pengine = &falcon->engine; + + return nvkm_engine_ctor(&nvkm_falcon, device, type, inst, enable, &falcon->engine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild new file mode 100644 index 000000000..5e831d347 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/fifo/base.o +nvkm-y += nvkm/engine/fifo/nv04.o +nvkm-y += nvkm/engine/fifo/nv10.o +nvkm-y += nvkm/engine/fifo/nv17.o +nvkm-y += nvkm/engine/fifo/nv40.o +nvkm-y += nvkm/engine/fifo/nv50.o +nvkm-y += nvkm/engine/fifo/g84.o +nvkm-y += nvkm/engine/fifo/gf100.o +nvkm-y += nvkm/engine/fifo/gk104.o +nvkm-y += nvkm/engine/fifo/gk110.o +nvkm-y += nvkm/engine/fifo/gk208.o +nvkm-y += nvkm/engine/fifo/gk20a.o +nvkm-y += nvkm/engine/fifo/gm107.o +nvkm-y += nvkm/engine/fifo/gm200.o +nvkm-y += nvkm/engine/fifo/gm20b.o +nvkm-y += nvkm/engine/fifo/gp100.o +nvkm-y += nvkm/engine/fifo/gp10b.o +nvkm-y += nvkm/engine/fifo/gv100.o +nvkm-y += nvkm/engine/fifo/tu102.o +nvkm-y += nvkm/engine/fifo/ga102.o + +nvkm-y += nvkm/engine/fifo/chan.o +nvkm-y += nvkm/engine/fifo/channv50.o +nvkm-y += nvkm/engine/fifo/chang84.o + +nvkm-y += nvkm/engine/fifo/dmanv04.o +nvkm-y += nvkm/engine/fifo/dmanv10.o +nvkm-y += nvkm/engine/fifo/dmanv17.o +nvkm-y += nvkm/engine/fifo/dmanv40.o + +nvkm-y += nvkm/engine/fifo/gpfifonv50.o +nvkm-y += nvkm/engine/fifo/gpfifog84.o +nvkm-y += nvkm/engine/fifo/gpfifogf100.o +nvkm-y += nvkm/engine/fifo/gpfifogk104.o +nvkm-y += nvkm/engine/fifo/gpfifogv100.o +nvkm-y += nvkm/engine/fifo/gpfifotu102.o + +nvkm-y += nvkm/engine/fifo/usergv100.o +nvkm-y += nvkm/engine/fifo/usertu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c new file mode 100644 index 000000000..58b8df75f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c @@ -0,0 +1,357 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" + +#include +#include +#include +#include + +#include +#include +#include + +void +nvkm_fifo_recover_chan(struct nvkm_fifo *fifo, int chid) +{ + unsigned long flags; + if (WARN_ON(!fifo->func->recover_chan)) + return; + spin_lock_irqsave(&fifo->lock, flags); + fifo->func->recover_chan(fifo, chid); + spin_unlock_irqrestore(&fifo->lock, flags); +} + +void +nvkm_fifo_pause(struct nvkm_fifo *fifo, unsigned long *flags) +{ + return fifo->func->pause(fifo, flags); +} + +void +nvkm_fifo_start(struct nvkm_fifo *fifo, unsigned long *flags) +{ + return fifo->func->start(fifo, flags); +} + +void +nvkm_fifo_fault(struct nvkm_fifo *fifo, struct nvkm_fault_data *info) +{ + return fifo->func->fault(fifo, info); +} + +void +nvkm_fifo_chan_put(struct nvkm_fifo *fifo, unsigned long flags, + struct nvkm_fifo_chan **pchan) +{ + struct nvkm_fifo_chan *chan = *pchan; + if (likely(chan)) { + *pchan = NULL; + spin_unlock_irqrestore(&fifo->lock, flags); + } +} + +struct nvkm_fifo_chan * +nvkm_fifo_chan_inst_locked(struct nvkm_fifo *fifo, u64 inst) +{ + struct nvkm_fifo_chan *chan; + list_for_each_entry(chan, &fifo->chan, head) { + if (chan->inst->addr == inst) { + list_del(&chan->head); + list_add(&chan->head, &fifo->chan); + return chan; + } + } + return NULL; +} + +struct nvkm_fifo_chan * +nvkm_fifo_chan_inst(struct nvkm_fifo *fifo, u64 inst, unsigned long *rflags) +{ + struct nvkm_fifo_chan *chan; + unsigned long flags; + spin_lock_irqsave(&fifo->lock, flags); + if ((chan = nvkm_fifo_chan_inst_locked(fifo, inst))) { + *rflags = flags; + return chan; + } + spin_unlock_irqrestore(&fifo->lock, flags); + return NULL; +} + +struct nvkm_fifo_chan * +nvkm_fifo_chan_chid(struct nvkm_fifo *fifo, int chid, unsigned long *rflags) +{ + struct nvkm_fifo_chan *chan; + unsigned long flags; + spin_lock_irqsave(&fifo->lock, flags); + list_for_each_entry(chan, &fifo->chan, head) { + if (chan->chid == chid) { + list_del(&chan->head); + list_add(&chan->head, &fifo->chan); + *rflags = flags; + return chan; + } + } + spin_unlock_irqrestore(&fifo->lock, flags); + return NULL; +} + +void +nvkm_fifo_kevent(struct nvkm_fifo *fifo, int chid) +{ + nvkm_event_send(&fifo->kevent, 1, chid, NULL, 0); +} + +static int +nvkm_fifo_kevent_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + if (size == 0) { + notify->size = 0; + notify->types = 1; + notify->index = chan->chid; + return 0; + } + return -ENOSYS; +} + +static const struct nvkm_event_func +nvkm_fifo_kevent_func = { + .ctor = nvkm_fifo_kevent_ctor, +}; + +static void +nvkm_fifo_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + fifo->func->uevent_fini(fifo); +} + +static void +nvkm_fifo_uevent_init(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), uevent); + fifo->func->uevent_init(fifo); +} + +static int +nvkm_fifo_uevent_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + union { + struct nvif_notify_uevent_req none; + } *req = data; + int ret = -ENOSYS; + + if (!(ret = nvif_unvers(ret, &data, &size, req->none))) { + notify->size = sizeof(struct nvif_notify_uevent_rep); + notify->types = 1; + notify->index = 0; + } + + return ret; +} + +static const struct nvkm_event_func +nvkm_fifo_uevent_func = { + .ctor = nvkm_fifo_uevent_ctor, + .init = nvkm_fifo_uevent_init, + .fini = nvkm_fifo_uevent_fini, +}; + +void +nvkm_fifo_uevent(struct nvkm_fifo *fifo) +{ + struct nvif_notify_uevent_rep rep = { + }; + nvkm_event_send(&fifo->uevent, 1, 0, &rep, sizeof(rep)); +} + +static int +nvkm_fifo_class_new_(struct nvkm_device *device, + const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_fifo *fifo = nvkm_fifo(oclass->engine); + return fifo->func->class_new(fifo, oclass, data, size, pobject); +} + +static const struct nvkm_device_oclass +nvkm_fifo_class_ = { + .ctor = nvkm_fifo_class_new_, +}; + +static int +nvkm_fifo_class_new(struct nvkm_device *device, + const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + const struct nvkm_fifo_chan_oclass *sclass = oclass->engn; + struct nvkm_fifo *fifo = nvkm_fifo(oclass->engine); + return sclass->ctor(fifo, oclass, data, size, pobject); +} + +static const struct nvkm_device_oclass +nvkm_fifo_class = { + .ctor = nvkm_fifo_class_new, +}; + +static int +nvkm_fifo_class_get(struct nvkm_oclass *oclass, int index, + const struct nvkm_device_oclass **class) +{ + struct nvkm_fifo *fifo = nvkm_fifo(oclass->engine); + const struct nvkm_fifo_chan_oclass *sclass; + int c = 0; + + if (fifo->func->class_get) { + int ret = fifo->func->class_get(fifo, index, oclass); + if (ret == 0) + *class = &nvkm_fifo_class_; + return ret; + } + + while ((sclass = fifo->func->chan[c])) { + if (c++ == index) { + oclass->base = sclass->base; + oclass->engn = sclass; + *class = &nvkm_fifo_class; + return 0; + } + } + + return c; +} + +static void +nvkm_fifo_intr(struct nvkm_engine *engine) +{ + struct nvkm_fifo *fifo = nvkm_fifo(engine); + fifo->func->intr(fifo); +} + +static int +nvkm_fifo_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_fifo *fifo = nvkm_fifo(engine); + if (fifo->func->fini) + fifo->func->fini(fifo); + return 0; +} + +static int +nvkm_fifo_info(struct nvkm_engine *engine, u64 mthd, u64 *data) +{ + struct nvkm_fifo *fifo = nvkm_fifo(engine); + switch (mthd) { + case NV_DEVICE_HOST_CHANNELS: *data = fifo->nr; return 0; + default: + if (fifo->func->info) + return fifo->func->info(fifo, mthd, data); + break; + } + return -ENOSYS; +} + +static int +nvkm_fifo_oneinit(struct nvkm_engine *engine) +{ + struct nvkm_fifo *fifo = nvkm_fifo(engine); + if (fifo->func->oneinit) + return fifo->func->oneinit(fifo); + return 0; +} + +static void +nvkm_fifo_preinit(struct nvkm_engine *engine) +{ + nvkm_mc_reset(engine->subdev.device, NVKM_ENGINE_FIFO, 0); +} + +static int +nvkm_fifo_init(struct nvkm_engine *engine) +{ + struct nvkm_fifo *fifo = nvkm_fifo(engine); + fifo->func->init(fifo); + return 0; +} + +static void * +nvkm_fifo_dtor(struct nvkm_engine *engine) +{ + struct nvkm_fifo *fifo = nvkm_fifo(engine); + void *data = fifo; + if (fifo->func->dtor) + data = fifo->func->dtor(fifo); + nvkm_event_fini(&fifo->kevent); + nvkm_event_fini(&fifo->uevent); + mutex_destroy(&fifo->mutex); + return data; +} + +static const struct nvkm_engine_func +nvkm_fifo = { + .dtor = nvkm_fifo_dtor, + .preinit = nvkm_fifo_preinit, + .oneinit = nvkm_fifo_oneinit, + .info = nvkm_fifo_info, + .init = nvkm_fifo_init, + .fini = nvkm_fifo_fini, + .intr = nvkm_fifo_intr, + .base.sclass = nvkm_fifo_class_get, +}; + +int +nvkm_fifo_ctor(const struct nvkm_fifo_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, int nr, struct nvkm_fifo *fifo) +{ + int ret; + + fifo->func = func; + INIT_LIST_HEAD(&fifo->chan); + spin_lock_init(&fifo->lock); + mutex_init(&fifo->mutex); + + if (WARN_ON(fifo->nr > NVKM_FIFO_CHID_NR)) + fifo->nr = NVKM_FIFO_CHID_NR; + else + fifo->nr = nr; + bitmap_clear(fifo->mask, 0, fifo->nr); + + ret = nvkm_engine_ctor(&nvkm_fifo, device, type, inst, true, &fifo->engine); + if (ret) + return ret; + + if (func->uevent_init) { + ret = nvkm_event_init(&nvkm_fifo_uevent_func, 1, 1, + &fifo->uevent); + if (ret) + return ret; + } + + return nvkm_event_init(&nvkm_fifo_kevent_func, 1, nr, &fifo->kevent); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.h new file mode 100644 index 000000000..d0ac60b06 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.h @@ -0,0 +1,11 @@ +#ifndef __NVKM_FIFO_CGRP_H__ +#define __NVKM_FIFO_CGRP_H__ +#include "priv.h" + +struct nvkm_fifo_cgrp { + int id; + struct list_head head; + struct list_head chan; + int chan_nr; +}; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c new file mode 100644 index 000000000..2e7f32ceb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c @@ -0,0 +1,393 @@ +/* + * 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 "chan.h" + +#include +#include +#include +#include +#include + +struct nvkm_fifo_chan_object { + struct nvkm_oproxy oproxy; + struct nvkm_fifo_chan *chan; + int hash; +}; + +static struct nvkm_fifo_engn * +nvkm_fifo_chan_engn(struct nvkm_fifo_chan *chan, struct nvkm_engine *engine) +{ + int engi = chan->fifo->func->engine_id(chan->fifo, engine); + if (engi >= 0) + return &chan->engn[engi]; + return NULL; +} + +static int +nvkm_fifo_chan_child_fini(struct nvkm_oproxy *base, bool suspend) +{ + struct nvkm_fifo_chan_object *object = + container_of(base, typeof(*object), oproxy); + struct nvkm_engine *engine = object->oproxy.object->engine; + struct nvkm_fifo_chan *chan = object->chan; + struct nvkm_fifo_engn *engn = nvkm_fifo_chan_engn(chan, engine); + const char *name = engine->subdev.name; + int ret = 0; + + if (--engn->usecount) + return 0; + + if (chan->func->engine_fini) { + ret = chan->func->engine_fini(chan, engine, suspend); + if (ret) { + nvif_error(&chan->object, + "detach %s failed, %d\n", name, ret); + return ret; + } + } + + if (engn->object) { + ret = nvkm_object_fini(engn->object, suspend); + if (ret && suspend) + return ret; + } + + nvif_trace(&chan->object, "detached %s\n", name); + return ret; +} + +static int +nvkm_fifo_chan_child_init(struct nvkm_oproxy *base) +{ + struct nvkm_fifo_chan_object *object = + container_of(base, typeof(*object), oproxy); + struct nvkm_engine *engine = object->oproxy.object->engine; + struct nvkm_fifo_chan *chan = object->chan; + struct nvkm_fifo_engn *engn = nvkm_fifo_chan_engn(chan, engine); + const char *name = engine->subdev.name; + int ret; + + if (engn->usecount++) + return 0; + + if (engn->object) { + ret = nvkm_object_init(engn->object); + if (ret) + return ret; + } + + if (chan->func->engine_init) { + ret = chan->func->engine_init(chan, engine); + if (ret) { + nvif_error(&chan->object, + "attach %s failed, %d\n", name, ret); + return ret; + } + } + + nvif_trace(&chan->object, "attached %s\n", name); + return 0; +} + +static void +nvkm_fifo_chan_child_del(struct nvkm_oproxy *base) +{ + struct nvkm_fifo_chan_object *object = + container_of(base, typeof(*object), oproxy); + struct nvkm_engine *engine = object->oproxy.base.engine; + struct nvkm_fifo_chan *chan = object->chan; + struct nvkm_fifo_engn *engn = nvkm_fifo_chan_engn(chan, engine); + + if (chan->func->object_dtor) + chan->func->object_dtor(chan, object->hash); + + if (!--engn->refcount) { + if (chan->func->engine_dtor) + chan->func->engine_dtor(chan, engine); + nvkm_object_del(&engn->object); + if (chan->vmm) + atomic_dec(&chan->vmm->engref[engine->subdev.type]); + } +} + +static const struct nvkm_oproxy_func +nvkm_fifo_chan_child_func = { + .dtor[0] = nvkm_fifo_chan_child_del, + .init[0] = nvkm_fifo_chan_child_init, + .fini[0] = nvkm_fifo_chan_child_fini, +}; + +static int +nvkm_fifo_chan_child_new(const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_engine *engine = oclass->engine; + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(oclass->parent); + struct nvkm_fifo_engn *engn = nvkm_fifo_chan_engn(chan, engine); + struct nvkm_fifo_chan_object *object; + int ret = 0; + + if (!(object = kzalloc(sizeof(*object), GFP_KERNEL))) + return -ENOMEM; + nvkm_oproxy_ctor(&nvkm_fifo_chan_child_func, oclass, &object->oproxy); + object->chan = chan; + *pobject = &object->oproxy.base; + + if (!engn->refcount++) { + struct nvkm_oclass cclass = { + .client = oclass->client, + .engine = oclass->engine, + }; + + if (chan->vmm) + atomic_inc(&chan->vmm->engref[engine->subdev.type]); + + if (engine->func->fifo.cclass) { + ret = engine->func->fifo.cclass(chan, &cclass, + &engn->object); + } else + if (engine->func->cclass) { + ret = nvkm_object_new_(engine->func->cclass, &cclass, + NULL, 0, &engn->object); + } + if (ret) + return ret; + + if (chan->func->engine_ctor) { + ret = chan->func->engine_ctor(chan, oclass->engine, + engn->object); + if (ret) + return ret; + } + } + + ret = oclass->base.ctor(&(const struct nvkm_oclass) { + .base = oclass->base, + .engn = oclass->engn, + .handle = oclass->handle, + .object = oclass->object, + .client = oclass->client, + .parent = engn->object ? + engn->object : + oclass->parent, + .engine = engine, + }, data, size, &object->oproxy.object); + if (ret) + return ret; + + if (chan->func->object_ctor) { + object->hash = + chan->func->object_ctor(chan, object->oproxy.object); + if (object->hash < 0) + return object->hash; + } + + return 0; +} + +static int +nvkm_fifo_chan_child_get(struct nvkm_object *object, int index, + struct nvkm_oclass *oclass) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + struct nvkm_fifo *fifo = chan->fifo; + struct nvkm_engine *engine; + u32 engm = chan->engm; + int engi, ret, c; + + for (; c = 0, engi = __ffs(engm), engm; engm &= ~(1ULL << engi)) { + if (!(engine = fifo->func->id_engine(fifo, engi))) + continue; + oclass->engine = engine; + oclass->base.oclass = 0; + + if (engine->func->fifo.sclass) { + ret = engine->func->fifo.sclass(oclass, index); + if (oclass->base.oclass) { + if (!oclass->base.ctor) + oclass->base.ctor = nvkm_object_new; + oclass->ctor = nvkm_fifo_chan_child_new; + return 0; + } + + index -= ret; + continue; + } + + while (engine->func->sclass[c].oclass) { + if (c++ == index) { + oclass->base = engine->func->sclass[index]; + if (!oclass->base.ctor) + oclass->base.ctor = nvkm_object_new; + oclass->ctor = nvkm_fifo_chan_child_new; + return 0; + } + } + index -= c; + } + + return -EINVAL; +} + +static int +nvkm_fifo_chan_ntfy(struct nvkm_object *object, u32 type, + struct nvkm_event **pevent) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + if (chan->func->ntfy) + return chan->func->ntfy(chan, type, pevent); + return -ENODEV; +} + +static int +nvkm_fifo_chan_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + *type = NVKM_OBJECT_MAP_IO; + *addr = chan->addr; + *size = chan->size; + return 0; +} + +static int +nvkm_fifo_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + chan->func->fini(chan); + return 0; +} + +static int +nvkm_fifo_chan_init(struct nvkm_object *object) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + chan->func->init(chan); + return 0; +} + +static void * +nvkm_fifo_chan_dtor(struct nvkm_object *object) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + struct nvkm_fifo *fifo = chan->fifo; + void *data = chan->func->dtor(chan); + unsigned long flags; + + spin_lock_irqsave(&fifo->lock, flags); + if (!list_empty(&chan->head)) { + __clear_bit(chan->chid, fifo->mask); + list_del(&chan->head); + } + spin_unlock_irqrestore(&fifo->lock, flags); + + if (chan->vmm) { + nvkm_vmm_part(chan->vmm, chan->inst->memory); + nvkm_vmm_unref(&chan->vmm); + } + + nvkm_gpuobj_del(&chan->push); + nvkm_gpuobj_del(&chan->inst); + return data; +} + +static const struct nvkm_object_func +nvkm_fifo_chan_func = { + .dtor = nvkm_fifo_chan_dtor, + .init = nvkm_fifo_chan_init, + .fini = nvkm_fifo_chan_fini, + .ntfy = nvkm_fifo_chan_ntfy, + .map = nvkm_fifo_chan_map, + .sclass = nvkm_fifo_chan_child_get, +}; + +int +nvkm_fifo_chan_ctor(const struct nvkm_fifo_chan_func *func, + struct nvkm_fifo *fifo, u32 size, u32 align, bool zero, + u64 hvmm, u64 push, u32 engm, int bar, u32 base, + u32 user, const struct nvkm_oclass *oclass, + struct nvkm_fifo_chan *chan) +{ + struct nvkm_client *client = oclass->client; + struct nvkm_device *device = fifo->engine.subdev.device; + struct nvkm_dmaobj *dmaobj; + unsigned long flags; + int ret; + + nvkm_object_ctor(&nvkm_fifo_chan_func, oclass, &chan->object); + chan->func = func; + chan->fifo = fifo; + chan->engm = engm; + INIT_LIST_HEAD(&chan->head); + + /* instance memory */ + ret = nvkm_gpuobj_new(device, size, align, zero, NULL, &chan->inst); + if (ret) + return ret; + + /* allocate push buffer ctxdma instance */ + if (push) { + dmaobj = nvkm_dmaobj_search(client, push); + if (IS_ERR(dmaobj)) + return PTR_ERR(dmaobj); + + ret = nvkm_object_bind(&dmaobj->object, chan->inst, -16, + &chan->push); + if (ret) + return ret; + } + + /* channel address space */ + if (hvmm) { + struct nvkm_vmm *vmm = nvkm_uvmm_search(client, hvmm); + if (IS_ERR(vmm)) + return PTR_ERR(vmm); + + if (vmm->mmu != device->mmu) + return -EINVAL; + + ret = nvkm_vmm_join(vmm, chan->inst->memory); + if (ret) + return ret; + + chan->vmm = nvkm_vmm_ref(vmm); + } + + /* allocate channel id */ + spin_lock_irqsave(&fifo->lock, flags); + chan->chid = find_first_zero_bit(fifo->mask, NVKM_FIFO_CHID_NR); + if (chan->chid >= NVKM_FIFO_CHID_NR) { + spin_unlock_irqrestore(&fifo->lock, flags); + return -ENOSPC; + } + list_add(&chan->head, &fifo->chan); + __set_bit(chan->chid, fifo->mask); + spin_unlock_irqrestore(&fifo->lock, flags); + + /* determine address of this channel's user registers */ + chan->addr = device->func->resource_addr(device, bar) + + base + user * chan->chid; + chan->size = user; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h new file mode 100644 index 000000000..e53504354 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FIFO_CHAN_H__ +#define __NVKM_FIFO_CHAN_H__ +#define nvkm_fifo_chan(p) container_of((p), struct nvkm_fifo_chan, object) +#include "priv.h" + +struct nvkm_fifo_chan_func { + void *(*dtor)(struct nvkm_fifo_chan *); + void (*init)(struct nvkm_fifo_chan *); + void (*fini)(struct nvkm_fifo_chan *); + int (*ntfy)(struct nvkm_fifo_chan *, u32 type, struct nvkm_event **); + int (*engine_ctor)(struct nvkm_fifo_chan *, struct nvkm_engine *, + struct nvkm_object *); + void (*engine_dtor)(struct nvkm_fifo_chan *, struct nvkm_engine *); + int (*engine_init)(struct nvkm_fifo_chan *, struct nvkm_engine *); + int (*engine_fini)(struct nvkm_fifo_chan *, struct nvkm_engine *, + bool suspend); + int (*object_ctor)(struct nvkm_fifo_chan *, struct nvkm_object *); + void (*object_dtor)(struct nvkm_fifo_chan *, int); + u32 (*submit_token)(struct nvkm_fifo_chan *); +}; + +int nvkm_fifo_chan_ctor(const struct nvkm_fifo_chan_func *, struct nvkm_fifo *, + u32 size, u32 align, bool zero, u64 vm, u64 push, + u32 engm, int bar, u32 base, u32 user, + const struct nvkm_oclass *, struct nvkm_fifo_chan *); + +struct nvkm_fifo_chan_oclass { + int (*ctor)(struct nvkm_fifo *, const struct nvkm_oclass *, + void *data, u32 size, struct nvkm_object **); + struct nvkm_sclass base; +}; + +int gf100_fifo_chan_ntfy(struct nvkm_fifo_chan *, u32, struct nvkm_event **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c new file mode 100644 index 000000000..3492c561f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c @@ -0,0 +1,263 @@ +/* + * 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 "channv50.h" + +#include +#include +#include +#include + +#include + +static int +g84_fifo_chan_ntfy(struct nvkm_fifo_chan *chan, u32 type, + struct nvkm_event **pevent) +{ + switch (type) { + case NV826E_V0_NTFY_NON_STALL_INTERRUPT: + *pevent = &chan->fifo->uevent; + return 0; + default: + break; + } + return -EINVAL; +} + +static int +g84_fifo_chan_engine_addr(struct nvkm_engine *engine) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_DMAOBJ: + case NVKM_ENGINE_SW : return -1; + case NVKM_ENGINE_GR : return 0x0020; + case NVKM_ENGINE_VP : + case NVKM_ENGINE_MSPDEC: return 0x0040; + case NVKM_ENGINE_MPEG : + case NVKM_ENGINE_MSPPP : return 0x0060; + case NVKM_ENGINE_BSP : + case NVKM_ENGINE_MSVLD : return 0x0080; + case NVKM_ENGINE_CIPHER: + case NVKM_ENGINE_SEC : return 0x00a0; + case NVKM_ENGINE_CE : return 0x00c0; + default: + WARN_ON(1); + return -1; + } +} + +static int +g84_fifo_chan_engine_fini(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, bool suspend) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + struct nv50_fifo *fifo = chan->fifo; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 engn, save; + int offset; + bool done; + + offset = g84_fifo_chan_engine_addr(engine); + if (offset < 0) + return 0; + + engn = fifo->base.func->engine_id(&fifo->base, engine) - 1; + save = nvkm_mask(device, 0x002520, 0x0000003f, 1 << engn); + nvkm_wr32(device, 0x0032fc, chan->base.inst->addr >> 12); + done = nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x0032fc) != 0xffffffff) + break; + ) >= 0; + nvkm_wr32(device, 0x002520, save); + if (!done) { + nvkm_error(subdev, "channel %d [%s] unload timeout\n", + chan->base.chid, chan->base.object.client->name); + if (suspend) + return -EBUSY; + } + + nvkm_kmap(chan->eng); + nvkm_wo32(chan->eng, offset + 0x00, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x04, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x08, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x0c, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x10, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x14, 0x00000000); + nvkm_done(chan->eng); + return 0; +} + + +static int +g84_fifo_chan_engine_init(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + struct nvkm_gpuobj *engn = *nv50_fifo_chan_engine(chan, engine); + u64 limit, start; + int offset; + + offset = g84_fifo_chan_engine_addr(engine); + if (offset < 0) + return 0; + limit = engn->addr + engn->size - 1; + start = engn->addr; + + nvkm_kmap(chan->eng); + nvkm_wo32(chan->eng, offset + 0x00, 0x00190000); + nvkm_wo32(chan->eng, offset + 0x04, lower_32_bits(limit)); + nvkm_wo32(chan->eng, offset + 0x08, lower_32_bits(start)); + nvkm_wo32(chan->eng, offset + 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nvkm_wo32(chan->eng, offset + 0x10, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x14, 0x00000000); + nvkm_done(chan->eng); + return 0; +} + +static int +g84_fifo_chan_engine_ctor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, + struct nvkm_object *object) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + + if (g84_fifo_chan_engine_addr(engine) < 0) + return 0; + + return nvkm_object_bind(object, NULL, 0, nv50_fifo_chan_engine(chan, engine)); +} + +static int +g84_fifo_chan_object_ctor(struct nvkm_fifo_chan *base, + struct nvkm_object *object) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + u32 handle = object->handle; + u32 context; + + switch (object->engine->subdev.type) { + case NVKM_ENGINE_DMAOBJ: + case NVKM_ENGINE_SW : context = 0x00000000; break; + case NVKM_ENGINE_GR : context = 0x00100000; break; + case NVKM_ENGINE_MPEG : + case NVKM_ENGINE_MSPPP : context = 0x00200000; break; + case NVKM_ENGINE_ME : + case NVKM_ENGINE_CE : context = 0x00300000; break; + case NVKM_ENGINE_VP : + case NVKM_ENGINE_MSPDEC: context = 0x00400000; break; + case NVKM_ENGINE_CIPHER: + case NVKM_ENGINE_SEC : + case NVKM_ENGINE_VIC : context = 0x00500000; break; + case NVKM_ENGINE_BSP : + case NVKM_ENGINE_MSVLD : context = 0x00600000; break; + default: + WARN_ON(1); + return -EINVAL; + } + + return nvkm_ramht_insert(chan->ramht, object, 0, 4, handle, context); +} + +static void +g84_fifo_chan_init(struct nvkm_fifo_chan *base) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + struct nv50_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u64 addr = chan->ramfc->addr >> 8; + u32 chid = chan->base.chid; + + nvkm_wr32(device, 0x002600 + (chid * 4), 0x80000000 | addr); + nv50_fifo_runlist_update(fifo); +} + +static const struct nvkm_fifo_chan_func +g84_fifo_chan_func = { + .dtor = nv50_fifo_chan_dtor, + .init = g84_fifo_chan_init, + .fini = nv50_fifo_chan_fini, + .ntfy = g84_fifo_chan_ntfy, + .engine_ctor = g84_fifo_chan_engine_ctor, + .engine_dtor = nv50_fifo_chan_engine_dtor, + .engine_init = g84_fifo_chan_engine_init, + .engine_fini = g84_fifo_chan_engine_fini, + .object_ctor = g84_fifo_chan_object_ctor, + .object_dtor = nv50_fifo_chan_object_dtor, +}; + +int +g84_fifo_chan_ctor(struct nv50_fifo *fifo, u64 vmm, u64 push, + const struct nvkm_oclass *oclass, + struct nv50_fifo_chan *chan) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + int ret; + + if (!vmm) + return -EINVAL; + + ret = nvkm_fifo_chan_ctor(&g84_fifo_chan_func, &fifo->base, + 0x10000, 0x1000, false, vmm, push, + BIT(G84_FIFO_ENGN_SW) | + BIT(G84_FIFO_ENGN_GR) | + BIT(G84_FIFO_ENGN_MPEG) | + BIT(G84_FIFO_ENGN_MSPPP) | + BIT(G84_FIFO_ENGN_ME) | + BIT(G84_FIFO_ENGN_CE0) | + BIT(G84_FIFO_ENGN_VP) | + BIT(G84_FIFO_ENGN_MSPDEC) | + BIT(G84_FIFO_ENGN_CIPHER) | + BIT(G84_FIFO_ENGN_SEC) | + BIT(G84_FIFO_ENGN_VIC) | + BIT(G84_FIFO_ENGN_BSP) | + BIT(G84_FIFO_ENGN_MSVLD) | + BIT(G84_FIFO_ENGN_DMA), + 0, 0xc00000, 0x2000, oclass, &chan->base); + chan->fifo = fifo; + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x0200, 0, true, chan->base.inst, + &chan->eng); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x4000, 0, false, chan->base.inst, + &chan->pgd); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x1000, 0x400, true, chan->base.inst, + &chan->cache); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x100, 0x100, true, chan->base.inst, + &chan->ramfc); + if (ret) + return ret; + + return nvkm_ramht_new(device, 0x8000, 16, chan->base.inst, &chan->ramht); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/changf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/changf100.h new file mode 100644 index 000000000..f7ac1061f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/changf100.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __GF100_FIFO_CHAN_H__ +#define __GF100_FIFO_CHAN_H__ +#define gf100_fifo_chan(p) container_of((p), struct gf100_fifo_chan, base) +#include "chan.h" +#include "gf100.h" + +struct gf100_fifo_chan { + struct nvkm_fifo_chan base; + struct gf100_fifo *fifo; + + struct list_head head; + bool killed; + +#define GF100_FIFO_ENGN_GR 0 +#define GF100_FIFO_ENGN_MSPDEC 1 +#define GF100_FIFO_ENGN_MSPPP 2 +#define GF100_FIFO_ENGN_MSVLD 3 +#define GF100_FIFO_ENGN_CE0 4 +#define GF100_FIFO_ENGN_CE1 5 +#define GF100_FIFO_ENGN_SW 15 + struct gf100_fifo_engn { + struct nvkm_gpuobj *inst; + struct nvkm_vma *vma; + } engn[NVKM_FIFO_ENGN_NR]; +}; + +extern const struct nvkm_fifo_chan_oclass gf100_fifo_gpfifo_oclass; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/changk104.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/changk104.h new file mode 100644 index 000000000..9713daee6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/changk104.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __GK104_FIFO_CHAN_H__ +#define __GK104_FIFO_CHAN_H__ +#define gk104_fifo_chan(p) container_of((p), struct gk104_fifo_chan, base) +#include "chan.h" +#include "gk104.h" + +struct gk104_fifo_chan { + struct nvkm_fifo_chan base; + struct gk104_fifo *fifo; + int runl; + + struct nvkm_fifo_cgrp *cgrp; + struct list_head head; + bool killed; + +#define GK104_FIFO_ENGN_SW 15 + struct gk104_fifo_engn { + struct nvkm_gpuobj *inst; + struct nvkm_vma *vma; + } engn[NVKM_FIFO_ENGN_NR]; +}; + +extern const struct nvkm_fifo_chan_func gk104_fifo_gpfifo_func; + +int gk104_fifo_gpfifo_new(struct gk104_fifo *, const struct nvkm_oclass *, + void *data, u32 size, struct nvkm_object **); +void *gk104_fifo_gpfifo_dtor(struct nvkm_fifo_chan *); +void gk104_fifo_gpfifo_init(struct nvkm_fifo_chan *); +void gk104_fifo_gpfifo_fini(struct nvkm_fifo_chan *); +struct gk104_fifo_engn *gk104_fifo_gpfifo_engine(struct gk104_fifo_chan *, struct nvkm_engine *); +int gk104_fifo_gpfifo_engine_ctor(struct nvkm_fifo_chan *, struct nvkm_engine *, + struct nvkm_object *); +void gk104_fifo_gpfifo_engine_dtor(struct nvkm_fifo_chan *, + struct nvkm_engine *); +int gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *); +int gk104_fifo_gpfifo_kick_locked(struct gk104_fifo_chan *); + +int gv100_fifo_gpfifo_new(struct gk104_fifo *, const struct nvkm_oclass *, + void *data, u32 size, struct nvkm_object **); +int gv100_fifo_gpfifo_new_(const struct nvkm_fifo_chan_func *, + struct gk104_fifo *, u64 *, u16 *, u64, u64, u64, + u64 *, bool, u32 *, const struct nvkm_oclass *, + struct nvkm_object **); +int gv100_fifo_gpfifo_engine_init(struct nvkm_fifo_chan *, + struct nvkm_engine *); +int gv100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *, + struct nvkm_engine *, bool); + +int tu102_fifo_gpfifo_new(struct gk104_fifo *, const struct nvkm_oclass *, + void *data, u32 size, struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv04.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv04.h new file mode 100644 index 000000000..727bc8976 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv04.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV04_FIFO_CHAN_H__ +#define __NV04_FIFO_CHAN_H__ +#define nv04_fifo_chan(p) container_of((p), struct nv04_fifo_chan, base) +#include "chan.h" +#include "nv04.h" + +struct nv04_fifo_chan { + struct nvkm_fifo_chan base; + struct nv04_fifo *fifo; + u32 ramfc; +#define NV04_FIFO_ENGN_SW 0 +#define NV04_FIFO_ENGN_GR 1 +#define NV04_FIFO_ENGN_MPEG 2 +#define NV04_FIFO_ENGN_DMA 3 + struct nvkm_gpuobj *engn[NVKM_FIFO_ENGN_NR]; +}; + +extern const struct nvkm_fifo_chan_func nv04_fifo_dma_func; +void *nv04_fifo_dma_dtor(struct nvkm_fifo_chan *); +void nv04_fifo_dma_init(struct nvkm_fifo_chan *); +void nv04_fifo_dma_fini(struct nvkm_fifo_chan *); +void nv04_fifo_dma_object_dtor(struct nvkm_fifo_chan *, int); + +extern const struct nvkm_fifo_chan_oclass nv04_fifo_dma_oclass; +extern const struct nvkm_fifo_chan_oclass nv10_fifo_dma_oclass; +extern const struct nvkm_fifo_chan_oclass nv17_fifo_dma_oclass; +extern const struct nvkm_fifo_chan_oclass nv40_fifo_dma_oclass; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.c new file mode 100644 index 000000000..c44d7c81d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.c @@ -0,0 +1,276 @@ +/* + * 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 "channv50.h" + +#include +#include +#include +#include + +static int +nv50_fifo_chan_engine_addr(struct nvkm_engine *engine) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_DMAOBJ: + case NVKM_ENGINE_SW : return -1; + case NVKM_ENGINE_GR : return 0x0000; + case NVKM_ENGINE_MPEG : return 0x0060; + default: + WARN_ON(1); + return -1; + } +} + +struct nvkm_gpuobj ** +nv50_fifo_chan_engine(struct nv50_fifo_chan *chan, struct nvkm_engine *engine) +{ + int engi = chan->base.fifo->func->engine_id(chan->base.fifo, engine); + if (engi >= 0) + return &chan->engn[engi]; + return NULL; +} + +static int +nv50_fifo_chan_engine_fini(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, bool suspend) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + struct nv50_fifo *fifo = chan->fifo; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + int offset, ret = 0; + u32 me; + + offset = nv50_fifo_chan_engine_addr(engine); + if (offset < 0) + return 0; + + /* HW bug workaround: + * + * PFIFO will hang forever if the connected engines don't report + * that they've processed the context switch request. + * + * In order for the kickoff to work, we need to ensure all the + * connected engines are in a state where they can answer. + * + * Newer chipsets don't seem to suffer from this issue, and well, + * there's also a "ignore these engines" bitmask reg we can use + * if we hit the issue there.. + */ + me = nvkm_mask(device, 0x00b860, 0x00000001, 0x00000001); + + /* do the kickoff... */ + nvkm_wr32(device, 0x0032fc, chan->base.inst->addr >> 12); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x0032fc) != 0xffffffff) + break; + ) < 0) { + nvkm_error(subdev, "channel %d [%s] unload timeout\n", + chan->base.chid, chan->base.object.client->name); + if (suspend) + ret = -EBUSY; + } + nvkm_wr32(device, 0x00b860, me); + + if (ret == 0) { + nvkm_kmap(chan->eng); + nvkm_wo32(chan->eng, offset + 0x00, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x04, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x08, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x0c, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x10, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x14, 0x00000000); + nvkm_done(chan->eng); + } + + return ret; +} + +static int +nv50_fifo_chan_engine_init(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + struct nvkm_gpuobj *engn = *nv50_fifo_chan_engine(chan, engine); + u64 limit, start; + int offset; + + offset = nv50_fifo_chan_engine_addr(engine); + if (offset < 0) + return 0; + limit = engn->addr + engn->size - 1; + start = engn->addr; + + nvkm_kmap(chan->eng); + nvkm_wo32(chan->eng, offset + 0x00, 0x00190000); + nvkm_wo32(chan->eng, offset + 0x04, lower_32_bits(limit)); + nvkm_wo32(chan->eng, offset + 0x08, lower_32_bits(start)); + nvkm_wo32(chan->eng, offset + 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nvkm_wo32(chan->eng, offset + 0x10, 0x00000000); + nvkm_wo32(chan->eng, offset + 0x14, 0x00000000); + nvkm_done(chan->eng); + return 0; +} + +void +nv50_fifo_chan_engine_dtor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + nvkm_gpuobj_del(nv50_fifo_chan_engine(chan, engine)); +} + +static int +nv50_fifo_chan_engine_ctor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, + struct nvkm_object *object) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + + if (nv50_fifo_chan_engine_addr(engine) < 0) + return 0; + + return nvkm_object_bind(object, NULL, 0, nv50_fifo_chan_engine(chan, engine)); +} + +void +nv50_fifo_chan_object_dtor(struct nvkm_fifo_chan *base, int cookie) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + nvkm_ramht_remove(chan->ramht, cookie); +} + +static int +nv50_fifo_chan_object_ctor(struct nvkm_fifo_chan *base, + struct nvkm_object *object) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + u32 handle = object->handle; + u32 context; + + switch (object->engine->subdev.type) { + case NVKM_ENGINE_DMAOBJ: + case NVKM_ENGINE_SW : context = 0x00000000; break; + case NVKM_ENGINE_GR : context = 0x00100000; break; + case NVKM_ENGINE_MPEG : context = 0x00200000; break; + default: + WARN_ON(1); + return -EINVAL; + } + + return nvkm_ramht_insert(chan->ramht, object, 0, 4, handle, context); +} + +void +nv50_fifo_chan_fini(struct nvkm_fifo_chan *base) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + struct nv50_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 chid = chan->base.chid; + + /* remove channel from runlist, fifo will unload context */ + nvkm_mask(device, 0x002600 + (chid * 4), 0x80000000, 0x00000000); + nv50_fifo_runlist_update(fifo); + nvkm_wr32(device, 0x002600 + (chid * 4), 0x00000000); +} + +static void +nv50_fifo_chan_init(struct nvkm_fifo_chan *base) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + struct nv50_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u64 addr = chan->ramfc->addr >> 12; + u32 chid = chan->base.chid; + + nvkm_wr32(device, 0x002600 + (chid * 4), 0x80000000 | addr); + nv50_fifo_runlist_update(fifo); +} + +void * +nv50_fifo_chan_dtor(struct nvkm_fifo_chan *base) +{ + struct nv50_fifo_chan *chan = nv50_fifo_chan(base); + nvkm_ramht_del(&chan->ramht); + nvkm_gpuobj_del(&chan->pgd); + nvkm_gpuobj_del(&chan->eng); + nvkm_gpuobj_del(&chan->cache); + nvkm_gpuobj_del(&chan->ramfc); + return chan; +} + +static const struct nvkm_fifo_chan_func +nv50_fifo_chan_func = { + .dtor = nv50_fifo_chan_dtor, + .init = nv50_fifo_chan_init, + .fini = nv50_fifo_chan_fini, + .engine_ctor = nv50_fifo_chan_engine_ctor, + .engine_dtor = nv50_fifo_chan_engine_dtor, + .engine_init = nv50_fifo_chan_engine_init, + .engine_fini = nv50_fifo_chan_engine_fini, + .object_ctor = nv50_fifo_chan_object_ctor, + .object_dtor = nv50_fifo_chan_object_dtor, +}; + +int +nv50_fifo_chan_ctor(struct nv50_fifo *fifo, u64 vmm, u64 push, + const struct nvkm_oclass *oclass, + struct nv50_fifo_chan *chan) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + int ret; + + if (!vmm) + return -EINVAL; + + ret = nvkm_fifo_chan_ctor(&nv50_fifo_chan_func, &fifo->base, + 0x10000, 0x1000, false, vmm, push, + BIT(NV50_FIFO_ENGN_SW) | + BIT(NV50_FIFO_ENGN_GR) | + BIT(NV50_FIFO_ENGN_MPEG) | + BIT(NV50_FIFO_ENGN_DMA), + 0, 0xc00000, 0x2000, oclass, &chan->base); + chan->fifo = fifo; + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x0200, 0x1000, true, chan->base.inst, + &chan->ramfc); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x1200, 0, true, chan->base.inst, + &chan->eng); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x4000, 0, false, chan->base.inst, + &chan->pgd); + if (ret) + return ret; + + return nvkm_ramht_new(device, 0x8000, 16, chan->base.inst, &chan->ramht); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.h new file mode 100644 index 000000000..3a95730d7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_FIFO_CHAN_H__ +#define __NV50_FIFO_CHAN_H__ +#define nv50_fifo_chan(p) container_of((p), struct nv50_fifo_chan, base) +#include "chan.h" +#include "nv50.h" + +struct nv50_fifo_chan { + struct nv50_fifo *fifo; + struct nvkm_fifo_chan base; + + struct nvkm_gpuobj *ramfc; + struct nvkm_gpuobj *cache; + struct nvkm_gpuobj *eng; + struct nvkm_gpuobj *pgd; + struct nvkm_ramht *ramht; + +#define NV50_FIFO_ENGN_SW 0 +#define NV50_FIFO_ENGN_GR 1 +#define NV50_FIFO_ENGN_MPEG 2 +#define NV50_FIFO_ENGN_DMA 3 + +#define G84_FIFO_ENGN_SW 0 +#define G84_FIFO_ENGN_GR 1 +#define G84_FIFO_ENGN_MPEG 2 +#define G84_FIFO_ENGN_MSPPP 2 +#define G84_FIFO_ENGN_ME 3 +#define G84_FIFO_ENGN_CE0 3 +#define G84_FIFO_ENGN_VP 4 +#define G84_FIFO_ENGN_MSPDEC 4 +#define G84_FIFO_ENGN_CIPHER 5 +#define G84_FIFO_ENGN_SEC 5 +#define G84_FIFO_ENGN_VIC 5 +#define G84_FIFO_ENGN_BSP 6 +#define G84_FIFO_ENGN_MSVLD 6 +#define G84_FIFO_ENGN_DMA 7 + struct nvkm_gpuobj *engn[NVKM_FIFO_ENGN_NR]; +}; + +int nv50_fifo_chan_ctor(struct nv50_fifo *, u64 vmm, u64 push, + const struct nvkm_oclass *, struct nv50_fifo_chan *); +void *nv50_fifo_chan_dtor(struct nvkm_fifo_chan *); +void nv50_fifo_chan_fini(struct nvkm_fifo_chan *); +struct nvkm_gpuobj **nv50_fifo_chan_engine(struct nv50_fifo_chan *, struct nvkm_engine *); +void nv50_fifo_chan_engine_dtor(struct nvkm_fifo_chan *, struct nvkm_engine *); +void nv50_fifo_chan_object_dtor(struct nvkm_fifo_chan *, int); + +int g84_fifo_chan_ctor(struct nv50_fifo *, u64 vmm, u64 push, + const struct nvkm_oclass *, struct nv50_fifo_chan *); + +extern const struct nvkm_fifo_chan_oclass nv50_fifo_gpfifo_oclass; +extern const struct nvkm_fifo_chan_oclass g84_fifo_gpfifo_oclass; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c new file mode 100644 index 000000000..dbcdc5fab --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c @@ -0,0 +1,226 @@ +/* + * 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 "channv04.h" +#include "regsnv04.h" + +#include +#include +#include + +#include +#include +#include + +void +nv04_fifo_dma_object_dtor(struct nvkm_fifo_chan *base, int cookie) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nvkm_instmem *imem = chan->fifo->base.engine.subdev.device->imem; + + mutex_lock(&chan->fifo->base.mutex); + nvkm_ramht_remove(imem->ramht, cookie); + mutex_unlock(&chan->fifo->base.mutex); +} + +static int +nv04_fifo_dma_object_ctor(struct nvkm_fifo_chan *base, + struct nvkm_object *object) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nvkm_instmem *imem = chan->fifo->base.engine.subdev.device->imem; + u32 context = 0x80000000 | chan->base.chid << 24; + u32 handle = object->handle; + int hash; + + switch (object->engine->subdev.type) { + case NVKM_ENGINE_DMAOBJ: + case NVKM_ENGINE_SW : context |= 0x00000000; break; + case NVKM_ENGINE_GR : context |= 0x00010000; break; + case NVKM_ENGINE_MPEG : context |= 0x00020000; break; + default: + WARN_ON(1); + return -EINVAL; + } + + mutex_lock(&chan->fifo->base.mutex); + hash = nvkm_ramht_insert(imem->ramht, object, chan->base.chid, 4, + handle, context); + mutex_unlock(&chan->fifo->base.mutex); + return hash; +} + +void +nv04_fifo_dma_fini(struct nvkm_fifo_chan *base) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nv04_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_memory *fctx = device->imem->ramfc; + const struct nv04_fifo_ramfc *c; + unsigned long flags; + u32 mask = fifo->base.nr - 1; + u32 data = chan->ramfc; + u32 chid; + + /* prevent fifo context switches */ + spin_lock_irqsave(&fifo->base.lock, flags); + nvkm_wr32(device, NV03_PFIFO_CACHES, 0); + + /* if this channel is active, replace it with a null context */ + chid = nvkm_rd32(device, NV03_PFIFO_CACHE1_PUSH1) & mask; + if (chid == chan->base.chid) { + nvkm_mask(device, NV04_PFIFO_CACHE1_DMA_PUSH, 0x00000001, 0); + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH0, 0); + nvkm_mask(device, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0); + + c = fifo->ramfc; + nvkm_kmap(fctx); + do { + u32 rm = ((1ULL << c->bits) - 1) << c->regs; + u32 cm = ((1ULL << c->bits) - 1) << c->ctxs; + u32 rv = (nvkm_rd32(device, c->regp) & rm) >> c->regs; + u32 cv = (nvkm_ro32(fctx, c->ctxp + data) & ~cm); + nvkm_wo32(fctx, c->ctxp + data, cv | (rv << c->ctxs)); + } while ((++c)->bits); + nvkm_done(fctx); + + c = fifo->ramfc; + do { + nvkm_wr32(device, c->regp, 0x00000000); + } while ((++c)->bits); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_GET, 0); + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUT, 0); + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH1, mask); + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH0, 1); + nvkm_wr32(device, NV04_PFIFO_CACHE1_PULL0, 1); + } + + /* restore normal operation, after disabling dma mode */ + nvkm_mask(device, NV04_PFIFO_MODE, 1 << chan->base.chid, 0); + nvkm_wr32(device, NV03_PFIFO_CACHES, 1); + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +void +nv04_fifo_dma_init(struct nvkm_fifo_chan *base) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nv04_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 mask = 1 << chan->base.chid; + unsigned long flags; + spin_lock_irqsave(&fifo->base.lock, flags); + nvkm_mask(device, NV04_PFIFO_MODE, mask, mask); + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +void * +nv04_fifo_dma_dtor(struct nvkm_fifo_chan *base) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nv04_fifo *fifo = chan->fifo; + struct nvkm_instmem *imem = fifo->base.engine.subdev.device->imem; + const struct nv04_fifo_ramfc *c = fifo->ramfc; + + nvkm_kmap(imem->ramfc); + do { + nvkm_wo32(imem->ramfc, chan->ramfc + c->ctxp, 0x00000000); + } while ((++c)->bits); + nvkm_done(imem->ramfc); + return chan; +} + +const struct nvkm_fifo_chan_func +nv04_fifo_dma_func = { + .dtor = nv04_fifo_dma_dtor, + .init = nv04_fifo_dma_init, + .fini = nv04_fifo_dma_fini, + .object_ctor = nv04_fifo_dma_object_ctor, + .object_dtor = nv04_fifo_dma_object_dtor, +}; + +static int +nv04_fifo_dma_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo *fifo = nv04_fifo(base); + struct nv04_fifo_chan *chan = NULL; + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel dma vers %d pushbuf %llx " + "offset %08x\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + if (!args->v0.pushbuf) + return -EINVAL; + } else + return ret; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = nvkm_fifo_chan_ctor(&nv04_fifo_dma_func, &fifo->base, + 0x1000, 0x1000, false, 0, args->v0.pushbuf, + BIT(NV04_FIFO_ENGN_SW) | + BIT(NV04_FIFO_ENGN_GR) | + BIT(NV04_FIFO_ENGN_DMA), + 0, 0x800000, 0x10000, oclass, &chan->base); + chan->fifo = fifo; + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + chan->ramfc = chan->base.chid * 32; + + nvkm_kmap(imem->ramfc); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x00, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x04, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x08, chan->base.push->addr >> 4); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x10, + NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | +#ifdef __BIG_ENDIAN + NV_PFIFO_CACHE1_BIG_ENDIAN | +#endif + NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + nvkm_done(imem->ramfc); + return 0; +} + +const struct nvkm_fifo_chan_oclass +nv04_fifo_dma_oclass = { + .base.oclass = NV03_CHANNEL_DMA, + .base.minver = 0, + .base.maxver = 0, + .ctor = nv04_fifo_dma_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv10.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv10.c new file mode 100644 index 000000000..07d80d54a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv10.c @@ -0,0 +1,97 @@ +/* + * 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 "channv04.h" +#include "regsnv04.h" + +#include +#include +#include + +#include +#include +#include + +static int +nv10_fifo_dma_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo *fifo = nv04_fifo(base); + struct nv04_fifo_chan *chan = NULL; + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel dma vers %d pushbuf %llx " + "offset %08x\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + if (!args->v0.pushbuf) + return -EINVAL; + } else + return ret; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = nvkm_fifo_chan_ctor(&nv04_fifo_dma_func, &fifo->base, + 0x1000, 0x1000, false, 0, args->v0.pushbuf, + BIT(NV04_FIFO_ENGN_SW) | + BIT(NV04_FIFO_ENGN_GR) | + BIT(NV04_FIFO_ENGN_DMA), + 0, 0x800000, 0x10000, oclass, &chan->base); + chan->fifo = fifo; + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + chan->ramfc = chan->base.chid * 32; + + nvkm_kmap(imem->ramfc); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x00, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x04, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x0c, chan->base.push->addr >> 4); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x14, + NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | +#ifdef __BIG_ENDIAN + NV_PFIFO_CACHE1_BIG_ENDIAN | +#endif + NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + nvkm_done(imem->ramfc); + return 0; +} + +const struct nvkm_fifo_chan_oclass +nv10_fifo_dma_oclass = { + .base.oclass = NV10_CHANNEL_DMA, + .base.minver = 0, + .base.maxver = 0, + .ctor = nv10_fifo_dma_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv17.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv17.c new file mode 100644 index 000000000..edd70a114 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv17.c @@ -0,0 +1,98 @@ +/* + * 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 "channv04.h" +#include "regsnv04.h" + +#include +#include +#include + +#include +#include +#include + +static int +nv17_fifo_dma_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo *fifo = nv04_fifo(base); + struct nv04_fifo_chan *chan = NULL; + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel dma vers %d pushbuf %llx " + "offset %08x\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + if (!args->v0.pushbuf) + return -EINVAL; + } else + return ret; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = nvkm_fifo_chan_ctor(&nv04_fifo_dma_func, &fifo->base, + 0x1000, 0x1000, false, 0, args->v0.pushbuf, + BIT(NV04_FIFO_ENGN_SW) | + BIT(NV04_FIFO_ENGN_GR) | + BIT(NV04_FIFO_ENGN_MPEG) | /* NV31- */ + BIT(NV04_FIFO_ENGN_DMA), + 0, 0x800000, 0x10000, oclass, &chan->base); + chan->fifo = fifo; + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + chan->ramfc = chan->base.chid * 64; + + nvkm_kmap(imem->ramfc); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x00, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x04, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x0c, chan->base.push->addr >> 4); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x14, + NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | +#ifdef __BIG_ENDIAN + NV_PFIFO_CACHE1_BIG_ENDIAN | +#endif + NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + nvkm_done(imem->ramfc); + return 0; +} + +const struct nvkm_fifo_chan_oclass +nv17_fifo_dma_oclass = { + .base.oclass = NV17_CHANNEL_DMA, + .base.minver = 0, + .base.maxver = 0, + .ctor = nv17_fifo_dma_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv40.c new file mode 100644 index 000000000..0411fb908 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv40.c @@ -0,0 +1,254 @@ +/* + * 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 "channv04.h" +#include "regsnv04.h" + +#include +#include +#include + +#include +#include +#include + +static bool +nv40_fifo_dma_engine(struct nvkm_engine *engine, u32 *reg, u32 *ctx) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_DMAOBJ: + case NVKM_ENGINE_SW: + return false; + case NVKM_ENGINE_GR: + *reg = 0x0032e0; + *ctx = 0x38; + return true; + case NVKM_ENGINE_MPEG: + if (engine->subdev.device->chipset < 0x44) + return false; + *reg = 0x00330c; + *ctx = 0x54; + return true; + default: + WARN_ON(1); + return false; + } +} + +static struct nvkm_gpuobj ** +nv40_fifo_dma_engn(struct nv04_fifo_chan *chan, struct nvkm_engine *engine) +{ + int engi = chan->base.fifo->func->engine_id(chan->base.fifo, engine); + if (engi >= 0) + return &chan->engn[engi]; + return NULL; +} + +static int +nv40_fifo_dma_engine_fini(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, bool suspend) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nv04_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + unsigned long flags; + u32 reg, ctx; + int chid; + + if (!nv40_fifo_dma_engine(engine, ®, &ctx)) + return 0; + + spin_lock_irqsave(&fifo->base.lock, flags); + nvkm_mask(device, 0x002500, 0x00000001, 0x00000000); + + chid = nvkm_rd32(device, 0x003204) & (fifo->base.nr - 1); + if (chid == chan->base.chid) + nvkm_wr32(device, reg, 0x00000000); + nvkm_kmap(imem->ramfc); + nvkm_wo32(imem->ramfc, chan->ramfc + ctx, 0x00000000); + nvkm_done(imem->ramfc); + + nvkm_mask(device, 0x002500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&fifo->base.lock, flags); + return 0; +} + +static int +nv40_fifo_dma_engine_init(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nv04_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + unsigned long flags; + u32 inst, reg, ctx; + int chid; + + if (!nv40_fifo_dma_engine(engine, ®, &ctx)) + return 0; + inst = (*nv40_fifo_dma_engn(chan, engine))->addr >> 4; + + spin_lock_irqsave(&fifo->base.lock, flags); + nvkm_mask(device, 0x002500, 0x00000001, 0x00000000); + + chid = nvkm_rd32(device, 0x003204) & (fifo->base.nr - 1); + if (chid == chan->base.chid) + nvkm_wr32(device, reg, inst); + nvkm_kmap(imem->ramfc); + nvkm_wo32(imem->ramfc, chan->ramfc + ctx, inst); + nvkm_done(imem->ramfc); + + nvkm_mask(device, 0x002500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&fifo->base.lock, flags); + return 0; +} + +static void +nv40_fifo_dma_engine_dtor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + nvkm_gpuobj_del(nv40_fifo_dma_engn(chan, engine)); +} + +static int +nv40_fifo_dma_engine_ctor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, + struct nvkm_object *object) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + u32 reg, ctx; + + if (!nv40_fifo_dma_engine(engine, ®, &ctx)) + return 0; + + return nvkm_object_bind(object, NULL, 0, nv40_fifo_dma_engn(chan, engine)); +} + +static int +nv40_fifo_dma_object_ctor(struct nvkm_fifo_chan *base, + struct nvkm_object *object) +{ + struct nv04_fifo_chan *chan = nv04_fifo_chan(base); + struct nvkm_instmem *imem = chan->fifo->base.engine.subdev.device->imem; + u32 context = chan->base.chid << 23; + u32 handle = object->handle; + int hash; + + switch (object->engine->subdev.type) { + case NVKM_ENGINE_DMAOBJ: + case NVKM_ENGINE_SW : context |= 0x00000000; break; + case NVKM_ENGINE_GR : context |= 0x00100000; break; + case NVKM_ENGINE_MPEG : context |= 0x00200000; break; + default: + WARN_ON(1); + return -EINVAL; + } + + mutex_lock(&chan->fifo->base.mutex); + hash = nvkm_ramht_insert(imem->ramht, object, chan->base.chid, 4, + handle, context); + mutex_unlock(&chan->fifo->base.mutex); + return hash; +} + +static const struct nvkm_fifo_chan_func +nv40_fifo_dma_func = { + .dtor = nv04_fifo_dma_dtor, + .init = nv04_fifo_dma_init, + .fini = nv04_fifo_dma_fini, + .engine_ctor = nv40_fifo_dma_engine_ctor, + .engine_dtor = nv40_fifo_dma_engine_dtor, + .engine_init = nv40_fifo_dma_engine_init, + .engine_fini = nv40_fifo_dma_engine_fini, + .object_ctor = nv40_fifo_dma_object_ctor, + .object_dtor = nv04_fifo_dma_object_dtor, +}; + +static int +nv40_fifo_dma_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct nv03_channel_dma_v0 v0; + } *args = data; + struct nv04_fifo *fifo = nv04_fifo(base); + struct nv04_fifo_chan *chan = NULL; + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel dma size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel dma vers %d pushbuf %llx " + "offset %08x\n", args->v0.version, + args->v0.pushbuf, args->v0.offset); + if (!args->v0.pushbuf) + return -EINVAL; + } else + return ret; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = nvkm_fifo_chan_ctor(&nv40_fifo_dma_func, &fifo->base, + 0x1000, 0x1000, false, 0, args->v0.pushbuf, + BIT(NV04_FIFO_ENGN_SW) | + BIT(NV04_FIFO_ENGN_GR) | + BIT(NV04_FIFO_ENGN_MPEG) | + BIT(NV04_FIFO_ENGN_DMA), + 0, 0xc00000, 0x1000, oclass, &chan->base); + chan->fifo = fifo; + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + chan->ramfc = chan->base.chid * 128; + + nvkm_kmap(imem->ramfc); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x00, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x04, args->v0.offset); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x0c, chan->base.push->addr >> 4); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x18, 0x30000000 | + NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | +#ifdef __BIG_ENDIAN + NV_PFIFO_CACHE1_BIG_ENDIAN | +#endif + NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + nvkm_wo32(imem->ramfc, chan->ramfc + 0x3c, 0x0001ffff); + nvkm_done(imem->ramfc); + return 0; +} + +const struct nvkm_fifo_chan_oclass +nv40_fifo_dma_oclass = { + .base.oclass = NV40_CHANNEL_DMA, + .base.minver = 0, + .base.maxver = 0, + .ctor = nv40_fifo_dma_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c new file mode 100644 index 000000000..3885c3830 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c @@ -0,0 +1,132 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "channv50.h" + +static void +g84_fifo_uevent_fini(struct nvkm_fifo *fifo) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + nvkm_mask(device, 0x002140, 0x40000000, 0x00000000); +} + +static void +g84_fifo_uevent_init(struct nvkm_fifo *fifo) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + nvkm_mask(device, 0x002140, 0x40000000, 0x40000000); +} + +static struct nvkm_engine * +g84_fifo_id_engine(struct nvkm_fifo *fifo, int engi) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + struct nvkm_engine *engine; + enum nvkm_subdev_type type; + + switch (engi) { + case G84_FIFO_ENGN_SW : type = NVKM_ENGINE_SW; break; + case G84_FIFO_ENGN_GR : type = NVKM_ENGINE_GR; break; + case G84_FIFO_ENGN_MPEG : + if ((engine = nvkm_device_engine(device, NVKM_ENGINE_MSPPP, 0))) + return engine; + type = NVKM_ENGINE_MPEG; + break; + case G84_FIFO_ENGN_ME : + if ((engine = nvkm_device_engine(device, NVKM_ENGINE_CE, 0))) + return engine; + type = NVKM_ENGINE_ME; + break; + case G84_FIFO_ENGN_VP : + if ((engine = nvkm_device_engine(device, NVKM_ENGINE_MSPDEC, 0))) + return engine; + type = NVKM_ENGINE_VP; + break; + case G84_FIFO_ENGN_CIPHER: + if ((engine = nvkm_device_engine(device, NVKM_ENGINE_VIC, 0))) + return engine; + if ((engine = nvkm_device_engine(device, NVKM_ENGINE_SEC, 0))) + return engine; + type = NVKM_ENGINE_CIPHER; + break; + case G84_FIFO_ENGN_BSP : + if ((engine = nvkm_device_engine(device, NVKM_ENGINE_MSVLD, 0))) + return engine; + type = NVKM_ENGINE_BSP; + break; + case G84_FIFO_ENGN_DMA : type = NVKM_ENGINE_DMAOBJ; break; + default: + WARN_ON(1); + return NULL; + } + + return nvkm_device_engine(fifo->engine.subdev.device, type, 0); +} + +static int +g84_fifo_engine_id(struct nvkm_fifo *base, struct nvkm_engine *engine) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_SW : return G84_FIFO_ENGN_SW; + case NVKM_ENGINE_GR : return G84_FIFO_ENGN_GR; + case NVKM_ENGINE_MPEG : + case NVKM_ENGINE_MSPPP : return G84_FIFO_ENGN_MPEG; + case NVKM_ENGINE_CE : return G84_FIFO_ENGN_CE0; + case NVKM_ENGINE_VP : + case NVKM_ENGINE_MSPDEC: return G84_FIFO_ENGN_VP; + case NVKM_ENGINE_CIPHER: + case NVKM_ENGINE_SEC : return G84_FIFO_ENGN_CIPHER; + case NVKM_ENGINE_BSP : + case NVKM_ENGINE_MSVLD : return G84_FIFO_ENGN_BSP; + case NVKM_ENGINE_DMAOBJ: return G84_FIFO_ENGN_DMA; + default: + WARN_ON(1); + return -1; + } +} + +static const struct nvkm_fifo_func +g84_fifo = { + .dtor = nv50_fifo_dtor, + .oneinit = nv50_fifo_oneinit, + .init = nv50_fifo_init, + .intr = nv04_fifo_intr, + .engine_id = g84_fifo_engine_id, + .id_engine = g84_fifo_id_engine, + .pause = nv04_fifo_pause, + .start = nv04_fifo_start, + .uevent_init = g84_fifo_uevent_init, + .uevent_fini = g84_fifo_uevent_fini, + .chan = { + &g84_fifo_gpfifo_oclass, + NULL + }, +}; + +int +g84_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return nv50_fifo_new_(&g84_fifo, device, type, inst, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga102.c new file mode 100644 index 000000000..c630dbd29 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga102.c @@ -0,0 +1,311 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#define ga102_fifo(p) container_of((p), struct ga102_fifo, base.engine) +#define ga102_chan(p) container_of((p), struct ga102_chan, object) +#include +#include "user.h" + +#include +#include +#include +#include + +#include +#include +#include + +struct ga102_fifo { + struct nvkm_fifo base; +}; + +struct ga102_chan { + struct nvkm_object object; + + struct { + u32 runl; + u32 chan; + } ctrl; + + struct nvkm_memory *mthd; + struct nvkm_memory *inst; + struct nvkm_memory *user; + struct nvkm_memory *runl; + + struct nvkm_vmm *vmm; +}; + +static int +ga102_chan_sclass(struct nvkm_object *object, int index, struct nvkm_oclass *oclass) +{ + if (index == 0) { + oclass->ctor = nvkm_object_new; + oclass->base = (struct nvkm_sclass) { -1, -1, AMPERE_DMA_COPY_B }; + return 0; + } + + return -EINVAL; +} + +static int +ga102_chan_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct ga102_chan *chan = ga102_chan(object); + struct nvkm_device *device = chan->object.engine->subdev.device; + u64 bar2 = nvkm_memory_bar2(chan->user); + + if (bar2 == ~0ULL) + return -EFAULT; + + *type = NVKM_OBJECT_MAP_IO; + *addr = device->func->resource_addr(device, 3) + bar2; + *size = 0x1000; + return 0; +} + +static int +ga102_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct ga102_chan *chan = ga102_chan(object); + struct nvkm_device *device = chan->object.engine->subdev.device; + + nvkm_wr32(device, chan->ctrl.chan, 0x00000003); + + nvkm_wr32(device, chan->ctrl.runl + 0x098, 0x01000000); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, chan->ctrl.runl + 0x098) & 0x00100000)) + break; + ); + + nvkm_wr32(device, chan->ctrl.runl + 0x088, 0); + + nvkm_wr32(device, chan->ctrl.chan, 0xffffffff); + return 0; +} + +static int +ga102_chan_init(struct nvkm_object *object) +{ + struct ga102_chan *chan = ga102_chan(object); + struct nvkm_device *device = chan->object.engine->subdev.device; + + nvkm_mask(device, chan->ctrl.runl + 0x300, 0x80000000, 0x80000000); + + nvkm_wr32(device, chan->ctrl.runl + 0x080, lower_32_bits(nvkm_memory_addr(chan->runl))); + nvkm_wr32(device, chan->ctrl.runl + 0x084, upper_32_bits(nvkm_memory_addr(chan->runl))); + nvkm_wr32(device, chan->ctrl.runl + 0x088, 2); + + nvkm_wr32(device, chan->ctrl.chan, 0x00000002); + nvkm_wr32(device, chan->ctrl.runl + 0x0090, 0); + return 0; +} + +static void * +ga102_chan_dtor(struct nvkm_object *object) +{ + struct ga102_chan *chan = ga102_chan(object); + + if (chan->vmm) { + nvkm_vmm_part(chan->vmm, chan->inst); + nvkm_vmm_unref(&chan->vmm); + } + + nvkm_memory_unref(&chan->runl); + nvkm_memory_unref(&chan->user); + nvkm_memory_unref(&chan->inst); + nvkm_memory_unref(&chan->mthd); + return chan; +} + +static const struct nvkm_object_func +ga102_chan = { + .dtor = ga102_chan_dtor, + .init = ga102_chan_init, + .fini = ga102_chan_fini, + .map = ga102_chan_map, + .sclass = ga102_chan_sclass, +}; + +static int +ga102_chan_new(struct nvkm_device *device, + const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject) +{ + struct volta_channel_gpfifo_a_v0 *args = argv; + struct nvkm_top_device *tdev; + struct nvkm_vmm *vmm; + struct ga102_chan *chan; + int ret; + + if (argc != sizeof(*args)) + return -ENOSYS; + + vmm = nvkm_uvmm_search(oclass->client, args->vmm); + if (IS_ERR(vmm)) + return PTR_ERR(vmm); + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + + nvkm_object_ctor(&ga102_chan, oclass, &chan->object); + *pobject = &chan->object; + + list_for_each_entry(tdev, &device->top->device, head) { + if (tdev->type == NVKM_ENGINE_CE) { + chan->ctrl.runl = tdev->runlist; + break; + } + } + + if (!chan->ctrl.runl) + return -ENODEV; + + chan->ctrl.chan = nvkm_rd32(device, chan->ctrl.runl + 0x004) & 0xfffffff0; + + args->chid = 0; + args->inst = 0; + args->token = nvkm_rd32(device, chan->ctrl.runl + 0x008) & 0xffff0000; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0x1000, true, &chan->mthd); + if (ret) + return ret; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0x1000, true, &chan->inst); + if (ret) + return ret; + + nvkm_kmap(chan->inst); + nvkm_wo32(chan->inst, 0x010, 0x0000face); + nvkm_wo32(chan->inst, 0x030, 0x7ffff902); + nvkm_wo32(chan->inst, 0x048, lower_32_bits(args->ioffset)); + nvkm_wo32(chan->inst, 0x04c, upper_32_bits(args->ioffset) | + (order_base_2(args->ilength / 8) << 16)); + nvkm_wo32(chan->inst, 0x084, 0x20400000); + nvkm_wo32(chan->inst, 0x094, 0x30000001); + nvkm_wo32(chan->inst, 0x0ac, 0x00020000); + nvkm_wo32(chan->inst, 0x0e4, 0x00000000); + nvkm_wo32(chan->inst, 0x0e8, 0); + nvkm_wo32(chan->inst, 0x0f4, 0x00001000); + nvkm_wo32(chan->inst, 0x0f8, 0x10003080); + nvkm_mo32(chan->inst, 0x218, 0x00000000, 0x00000000); + nvkm_wo32(chan->inst, 0x220, lower_32_bits(nvkm_memory_bar2(chan->mthd))); + nvkm_wo32(chan->inst, 0x224, upper_32_bits(nvkm_memory_bar2(chan->mthd))); + nvkm_done(chan->inst); + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0x1000, true, &chan->user); + if (ret) + return ret; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0x1000, true, &chan->runl); + if (ret) + return ret; + + nvkm_kmap(chan->runl); + nvkm_wo32(chan->runl, 0x00, 0x80030001); + nvkm_wo32(chan->runl, 0x04, 1); + nvkm_wo32(chan->runl, 0x08, 0); + nvkm_wo32(chan->runl, 0x0c, 0x00000000); + nvkm_wo32(chan->runl, 0x10, lower_32_bits(nvkm_memory_addr(chan->user))); + nvkm_wo32(chan->runl, 0x14, upper_32_bits(nvkm_memory_addr(chan->user))); + nvkm_wo32(chan->runl, 0x18, lower_32_bits(nvkm_memory_addr(chan->inst))); + nvkm_wo32(chan->runl, 0x1c, upper_32_bits(nvkm_memory_addr(chan->inst))); + nvkm_done(chan->runl); + + ret = nvkm_vmm_join(vmm, chan->inst); + if (ret) + return ret; + + chan->vmm = nvkm_vmm_ref(vmm); + return 0; +} + +static const struct nvkm_device_oclass +ga102_chan_oclass = { + .ctor = ga102_chan_new, +}; + +static int +ga102_user_new(struct nvkm_device *device, + const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject) +{ + return tu102_fifo_user_new(oclass, argv, argc, pobject); +} + +static const struct nvkm_device_oclass +ga102_user_oclass = { + .ctor = ga102_user_new, +}; + +static int +ga102_fifo_sclass(struct nvkm_oclass *oclass, int index, const struct nvkm_device_oclass **class) +{ + if (index == 0) { + oclass->base = (struct nvkm_sclass) { -1, -1, VOLTA_USERMODE_A }; + *class = &ga102_user_oclass; + return 0; + } else + if (index == 1) { + oclass->base = (struct nvkm_sclass) { 0, 0, AMPERE_CHANNEL_GPFIFO_B }; + *class = &ga102_chan_oclass; + return 0; + } + + return 2; +} + +static int +ga102_fifo_info(struct nvkm_engine *engine, u64 mthd, u64 *data) +{ + switch (mthd) { + case NV_DEVICE_HOST_CHANNELS: *data = 1; return 0; + default: + break; + } + + return -ENOSYS; +} + +static void * +ga102_fifo_dtor(struct nvkm_engine *engine) +{ + return ga102_fifo(engine); +} + +static const struct nvkm_engine_func +ga102_fifo = { + .dtor = ga102_fifo_dtor, + .info = ga102_fifo_info, + .base.sclass = ga102_fifo_sclass, +}; + +int +ga102_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + struct ga102_fifo *fifo; + + if (!(fifo = kzalloc(sizeof(*fifo), GFP_KERNEL))) + return -ENOMEM; + + nvkm_engine_ctor(&ga102_fifo, device, type, inst, true, &fifo->base.engine); + *pfifo = &fifo->base; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c new file mode 100644 index 000000000..8b4f36b3e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c @@ -0,0 +1,699 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "changf100.h" + +#include +#include +#include +#include +#include +#include + +#include + +static void +gf100_fifo_uevent_init(struct nvkm_fifo *fifo) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + nvkm_mask(device, 0x002140, 0x80000000, 0x80000000); +} + +static void +gf100_fifo_uevent_fini(struct nvkm_fifo *fifo) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + nvkm_mask(device, 0x002140, 0x80000000, 0x00000000); +} + +void +gf100_fifo_runlist_commit(struct gf100_fifo *fifo) +{ + struct gf100_fifo_chan *chan; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_memory *cur; + int nr = 0; + int target; + + mutex_lock(&fifo->base.mutex); + cur = fifo->runlist.mem[fifo->runlist.active]; + fifo->runlist.active = !fifo->runlist.active; + + nvkm_kmap(cur); + list_for_each_entry(chan, &fifo->chan, head) { + nvkm_wo32(cur, (nr * 8) + 0, chan->base.chid); + nvkm_wo32(cur, (nr * 8) + 4, 0x00000004); + nr++; + } + nvkm_done(cur); + + switch (nvkm_memory_target(cur)) { + case NVKM_MEM_TARGET_VRAM: target = 0; break; + case NVKM_MEM_TARGET_NCOH: target = 3; break; + default: + mutex_unlock(&fifo->base.mutex); + WARN_ON(1); + return; + } + + nvkm_wr32(device, 0x002270, (nvkm_memory_addr(cur) >> 12) | + (target << 28)); + nvkm_wr32(device, 0x002274, 0x01f00000 | nr); + + if (wait_event_timeout(fifo->runlist.wait, + !(nvkm_rd32(device, 0x00227c) & 0x00100000), + msecs_to_jiffies(2000)) == 0) + nvkm_error(subdev, "runlist update timeout\n"); + mutex_unlock(&fifo->base.mutex); +} + +void +gf100_fifo_runlist_remove(struct gf100_fifo *fifo, struct gf100_fifo_chan *chan) +{ + mutex_lock(&fifo->base.mutex); + list_del_init(&chan->head); + mutex_unlock(&fifo->base.mutex); +} + +void +gf100_fifo_runlist_insert(struct gf100_fifo *fifo, struct gf100_fifo_chan *chan) +{ + mutex_lock(&fifo->base.mutex); + list_add_tail(&chan->head, &fifo->chan); + mutex_unlock(&fifo->base.mutex); +} + +static struct nvkm_engine * +gf100_fifo_id_engine(struct nvkm_fifo *fifo, int engi) +{ + enum nvkm_subdev_type type; + int inst; + + switch (engi) { + case GF100_FIFO_ENGN_GR : type = NVKM_ENGINE_GR ; inst = 0; break; + case GF100_FIFO_ENGN_MSPDEC: type = NVKM_ENGINE_MSPDEC; inst = 0; break; + case GF100_FIFO_ENGN_MSPPP : type = NVKM_ENGINE_MSPPP ; inst = 0; break; + case GF100_FIFO_ENGN_MSVLD : type = NVKM_ENGINE_MSVLD ; inst = 0; break; + case GF100_FIFO_ENGN_CE0 : type = NVKM_ENGINE_CE ; inst = 0; break; + case GF100_FIFO_ENGN_CE1 : type = NVKM_ENGINE_CE ; inst = 1; break; + case GF100_FIFO_ENGN_SW : type = NVKM_ENGINE_SW ; inst = 0; break; + default: + WARN_ON(1); + return NULL; + } + + return nvkm_device_engine(fifo->engine.subdev.device, type, inst); +} + +static int +gf100_fifo_engine_id(struct nvkm_fifo *base, struct nvkm_engine *engine) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_GR : return GF100_FIFO_ENGN_GR; + case NVKM_ENGINE_MSPDEC: return GF100_FIFO_ENGN_MSPDEC; + case NVKM_ENGINE_MSPPP : return GF100_FIFO_ENGN_MSPPP; + case NVKM_ENGINE_MSVLD : return GF100_FIFO_ENGN_MSVLD; + case NVKM_ENGINE_CE : return GF100_FIFO_ENGN_CE0 + engine->subdev.inst; + case NVKM_ENGINE_SW : return GF100_FIFO_ENGN_SW; + default: + WARN_ON(1); + return -1; + } +} + +static void +gf100_fifo_recover_work(struct work_struct *w) +{ + struct gf100_fifo *fifo = container_of(w, typeof(*fifo), recover.work); + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_engine *engine; + unsigned long flags; + u32 engm, engn, todo; + + spin_lock_irqsave(&fifo->base.lock, flags); + engm = fifo->recover.mask; + fifo->recover.mask = 0ULL; + spin_unlock_irqrestore(&fifo->base.lock, flags); + + nvkm_mask(device, 0x002630, engm, engm); + + for (todo = engm; engn = __ffs(todo), todo; todo &= ~BIT_ULL(engn)) { + if ((engine = gf100_fifo_id_engine(&fifo->base, engn))) { + nvkm_subdev_fini(&engine->subdev, false); + WARN_ON(nvkm_subdev_init(&engine->subdev)); + } + } + + gf100_fifo_runlist_commit(fifo); + nvkm_wr32(device, 0x00262c, engm); + nvkm_mask(device, 0x002630, engm, 0x00000000); +} + +static void +gf100_fifo_recover(struct gf100_fifo *fifo, struct nvkm_engine *engine, + struct gf100_fifo_chan *chan) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 chid = chan->base.chid; + int engi = gf100_fifo_engine_id(&fifo->base, engine); + + nvkm_error(subdev, "%s engine fault on channel %d, recovering...\n", + engine->subdev.name, chid); + assert_spin_locked(&fifo->base.lock); + + nvkm_mask(device, 0x003004 + (chid * 0x08), 0x00000001, 0x00000000); + list_del_init(&chan->head); + chan->killed = true; + + if (engi >= 0 && engi != GF100_FIFO_ENGN_SW) + fifo->recover.mask |= BIT(engi); + schedule_work(&fifo->recover.work); + nvkm_fifo_kevent(&fifo->base, chid); +} + +static const struct nvkm_enum +gf100_fifo_fault_engine[] = { + { 0x00, "PGRAPH", NULL, NVKM_ENGINE_GR }, + { 0x03, "PEEPHOLE", NULL, NVKM_ENGINE_IFB }, + { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, + { 0x05, "BAR3", NULL, NVKM_SUBDEV_INSTMEM }, + { 0x07, "PFIFO", NULL, NVKM_ENGINE_FIFO }, + { 0x10, "PMSVLD", NULL, NVKM_ENGINE_MSVLD }, + { 0x11, "PMSPPP", NULL, NVKM_ENGINE_MSPPP }, + { 0x13, "PCOUNTER" }, + { 0x14, "PMSPDEC", NULL, NVKM_ENGINE_MSPDEC }, + { 0x15, "PCE0", NULL, NVKM_ENGINE_CE, 0 }, + { 0x16, "PCE1", NULL, NVKM_ENGINE_CE, 1 }, + { 0x17, "PMU" }, + {} +}; + +static const struct nvkm_enum +gf100_fifo_fault_reason[] = { + { 0x00, "PT_NOT_PRESENT" }, + { 0x01, "PT_TOO_SHORT" }, + { 0x02, "PAGE_NOT_PRESENT" }, + { 0x03, "VM_LIMIT_EXCEEDED" }, + { 0x04, "NO_CHANNEL" }, + { 0x05, "PAGE_SYSTEM_ONLY" }, + { 0x06, "PAGE_READ_ONLY" }, + { 0x0a, "COMPRESSED_SYSRAM" }, + { 0x0c, "INVALID_STORAGE_TYPE" }, + {} +}; + +static const struct nvkm_enum +gf100_fifo_fault_hubclient[] = { + { 0x01, "PCOPY0" }, + { 0x02, "PCOPY1" }, + { 0x04, "DISPATCH" }, + { 0x05, "CTXCTL" }, + { 0x06, "PFIFO" }, + { 0x07, "BAR_READ" }, + { 0x08, "BAR_WRITE" }, + { 0x0b, "PVP" }, + { 0x0c, "PMSPPP" }, + { 0x0d, "PMSVLD" }, + { 0x11, "PCOUNTER" }, + { 0x12, "PMU" }, + { 0x14, "CCACHE" }, + { 0x15, "CCACHE_POST" }, + {} +}; + +static const struct nvkm_enum +gf100_fifo_fault_gpcclient[] = { + { 0x01, "TEX" }, + { 0x0c, "ESETUP" }, + { 0x0e, "CTXCTL" }, + { 0x0f, "PROP" }, + {} +}; + +static void +gf100_fifo_fault(struct nvkm_fifo *base, struct nvkm_fault_data *info) +{ + struct gf100_fifo *fifo = gf100_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const struct nvkm_enum *er, *eu, *ec; + struct nvkm_engine *engine = NULL; + struct nvkm_fifo_chan *chan; + unsigned long flags; + char gpcid[8] = ""; + + er = nvkm_enum_find(gf100_fifo_fault_reason, info->reason); + eu = nvkm_enum_find(gf100_fifo_fault_engine, info->engine); + if (info->hub) { + ec = nvkm_enum_find(gf100_fifo_fault_hubclient, info->client); + } else { + ec = nvkm_enum_find(gf100_fifo_fault_gpcclient, info->client); + snprintf(gpcid, sizeof(gpcid), "GPC%d/", info->gpc); + } + + if (eu && eu->data2) { + switch (eu->data2) { + case NVKM_SUBDEV_BAR: + nvkm_bar_bar1_reset(device); + break; + case NVKM_SUBDEV_INSTMEM: + nvkm_bar_bar2_reset(device); + break; + case NVKM_ENGINE_IFB: + nvkm_mask(device, 0x001718, 0x00000000, 0x00000000); + break; + default: + engine = nvkm_device_engine(device, eu->data2, eu->inst); + break; + } + } + + chan = nvkm_fifo_chan_inst(&fifo->base, info->inst, &flags); + + nvkm_error(subdev, + "%s fault at %010llx engine %02x [%s] client %02x [%s%s] " + "reason %02x [%s] on channel %d [%010llx %s]\n", + info->access ? "write" : "read", info->addr, + info->engine, eu ? eu->name : "", + info->client, gpcid, ec ? ec->name : "", + info->reason, er ? er->name : "", chan ? chan->chid : -1, + info->inst, chan ? chan->object.client->name : "unknown"); + + if (engine && chan) + gf100_fifo_recover(fifo, engine, (void *)chan); + nvkm_fifo_chan_put(&fifo->base, flags, &chan); +} + +static const struct nvkm_enum +gf100_fifo_sched_reason[] = { + { 0x0a, "CTXSW_TIMEOUT" }, + {} +}; + +static void +gf100_fifo_intr_sched_ctxsw(struct gf100_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_engine *engine; + struct gf100_fifo_chan *chan; + unsigned long flags; + u32 engn; + + spin_lock_irqsave(&fifo->base.lock, flags); + for (engn = 0; engn < 6; engn++) { + u32 stat = nvkm_rd32(device, 0x002640 + (engn * 0x04)); + u32 busy = (stat & 0x80000000); + u32 save = (stat & 0x00100000); /* maybe? */ + u32 unk0 = (stat & 0x00040000); + u32 unk1 = (stat & 0x00001000); + u32 chid = (stat & 0x0000007f); + (void)save; + + if (busy && unk0 && unk1) { + list_for_each_entry(chan, &fifo->chan, head) { + if (chan->base.chid == chid) { + engine = gf100_fifo_id_engine(&fifo->base, engn); + if (!engine) + break; + gf100_fifo_recover(fifo, engine, chan); + break; + } + } + } + } + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +static void +gf100_fifo_intr_sched(struct gf100_fifo *fifo) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 intr = nvkm_rd32(device, 0x00254c); + u32 code = intr & 0x000000ff; + const struct nvkm_enum *en; + + en = nvkm_enum_find(gf100_fifo_sched_reason, code); + + nvkm_error(subdev, "SCHED_ERROR %02x [%s]\n", code, en ? en->name : ""); + + switch (code) { + case 0x0a: + gf100_fifo_intr_sched_ctxsw(fifo); + break; + default: + break; + } +} + +void +gf100_fifo_intr_fault(struct nvkm_fifo *fifo, int unit) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10)); + u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10)); + u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10)); + u32 type = nvkm_rd32(device, 0x00280c + (unit * 0x10)); + struct nvkm_fault_data info; + + info.inst = (u64)inst << 12; + info.addr = ((u64)vahi << 32) | valo; + info.time = 0; + info.engine = unit; + info.valid = 1; + info.gpc = (type & 0x1f000000) >> 24; + info.client = (type & 0x00001f00) >> 8; + info.access = (type & 0x00000080) >> 7; + info.hub = (type & 0x00000040) >> 6; + info.reason = (type & 0x0000000f); + + nvkm_fifo_fault(fifo, &info); +} + +static const struct nvkm_bitfield +gf100_fifo_pbdma_intr[] = { +/* { 0x00008000, "" } seen with null ib push */ + { 0x00200000, "ILLEGAL_MTHD" }, + { 0x00800000, "EMPTY_SUBC" }, + {} +}; + +static void +gf100_fifo_intr_pbdma(struct gf100_fifo *fifo, int unit) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x040108 + (unit * 0x2000)); + u32 addr = nvkm_rd32(device, 0x0400c0 + (unit * 0x2000)); + u32 data = nvkm_rd32(device, 0x0400c4 + (unit * 0x2000)); + u32 chid = nvkm_rd32(device, 0x040120 + (unit * 0x2000)) & 0x7f; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00003ffc); + struct nvkm_fifo_chan *chan; + unsigned long flags; + u32 show= stat; + char msg[128]; + + if (stat & 0x00800000) { + if (device->sw) { + if (nvkm_sw_mthd(device->sw, chid, subc, mthd, data)) + show &= ~0x00800000; + } + } + + if (show) { + nvkm_snprintbf(msg, sizeof(msg), gf100_fifo_pbdma_intr, show); + chan = nvkm_fifo_chan_chid(&fifo->base, chid, &flags); + nvkm_error(subdev, "PBDMA%d: %08x [%s] ch %d [%010llx %s] " + "subc %d mthd %04x data %08x\n", + unit, show, msg, chid, chan ? chan->inst->addr : 0, + chan ? chan->object.client->name : "unknown", + subc, mthd, data); + nvkm_fifo_chan_put(&fifo->base, flags, &chan); + } + + nvkm_wr32(device, 0x0400c0 + (unit * 0x2000), 0x80600008); + nvkm_wr32(device, 0x040108 + (unit * 0x2000), stat); +} + +static void +gf100_fifo_intr_runlist(struct gf100_fifo *fifo) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 intr = nvkm_rd32(device, 0x002a00); + + if (intr & 0x10000000) { + wake_up(&fifo->runlist.wait); + nvkm_wr32(device, 0x002a00, 0x10000000); + intr &= ~0x10000000; + } + + if (intr) { + nvkm_error(subdev, "RUNLIST %08x\n", intr); + nvkm_wr32(device, 0x002a00, intr); + } +} + +static void +gf100_fifo_intr_engine_unit(struct gf100_fifo *fifo, int engn) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 intr = nvkm_rd32(device, 0x0025a8 + (engn * 0x04)); + u32 inte = nvkm_rd32(device, 0x002628); + u32 unkn; + + nvkm_wr32(device, 0x0025a8 + (engn * 0x04), intr); + + for (unkn = 0; unkn < 8; unkn++) { + u32 ints = (intr >> (unkn * 0x04)) & inte; + if (ints & 0x1) { + nvkm_fifo_uevent(&fifo->base); + ints &= ~1; + } + if (ints) { + nvkm_error(subdev, "ENGINE %d %d %01x", + engn, unkn, ints); + nvkm_mask(device, 0x002628, ints, 0); + } + } +} + +void +gf100_fifo_intr_engine(struct gf100_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 mask = nvkm_rd32(device, 0x0025a4); + while (mask) { + u32 unit = __ffs(mask); + gf100_fifo_intr_engine_unit(fifo, unit); + mask &= ~(1 << unit); + } +} + +static void +gf100_fifo_intr(struct nvkm_fifo *base) +{ + struct gf100_fifo *fifo = gf100_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mask = nvkm_rd32(device, 0x002140); + u32 stat = nvkm_rd32(device, 0x002100) & mask; + + if (stat & 0x00000001) { + u32 intr = nvkm_rd32(device, 0x00252c); + nvkm_warn(subdev, "INTR 00000001: %08x\n", intr); + nvkm_wr32(device, 0x002100, 0x00000001); + stat &= ~0x00000001; + } + + if (stat & 0x00000100) { + gf100_fifo_intr_sched(fifo); + nvkm_wr32(device, 0x002100, 0x00000100); + stat &= ~0x00000100; + } + + if (stat & 0x00010000) { + u32 intr = nvkm_rd32(device, 0x00256c); + nvkm_warn(subdev, "INTR 00010000: %08x\n", intr); + nvkm_wr32(device, 0x002100, 0x00010000); + stat &= ~0x00010000; + } + + if (stat & 0x01000000) { + u32 intr = nvkm_rd32(device, 0x00258c); + nvkm_warn(subdev, "INTR 01000000: %08x\n", intr); + nvkm_wr32(device, 0x002100, 0x01000000); + stat &= ~0x01000000; + } + + if (stat & 0x10000000) { + u32 mask = nvkm_rd32(device, 0x00259c); + while (mask) { + u32 unit = __ffs(mask); + gf100_fifo_intr_fault(&fifo->base, unit); + nvkm_wr32(device, 0x00259c, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x10000000; + } + + if (stat & 0x20000000) { + u32 mask = nvkm_rd32(device, 0x0025a0); + while (mask) { + u32 unit = __ffs(mask); + gf100_fifo_intr_pbdma(fifo, unit); + nvkm_wr32(device, 0x0025a0, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x20000000; + } + + if (stat & 0x40000000) { + gf100_fifo_intr_runlist(fifo); + stat &= ~0x40000000; + } + + if (stat & 0x80000000) { + gf100_fifo_intr_engine(fifo); + stat &= ~0x80000000; + } + + if (stat) { + nvkm_error(subdev, "INTR %08x\n", stat); + nvkm_mask(device, 0x002140, stat, 0x00000000); + nvkm_wr32(device, 0x002100, stat); + } +} + +static int +gf100_fifo_oneinit(struct nvkm_fifo *base) +{ + struct gf100_fifo *fifo = gf100_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_vmm *bar = nvkm_bar_bar1_vmm(device); + int ret; + + /* Determine number of PBDMAs by checking valid enable bits. */ + nvkm_wr32(device, 0x002204, 0xffffffff); + fifo->pbdma_nr = hweight32(nvkm_rd32(device, 0x002204)); + nvkm_debug(subdev, "%d PBDMA(s)\n", fifo->pbdma_nr); + + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0x1000, + false, &fifo->runlist.mem[0]); + if (ret) + return ret; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0x1000, + false, &fifo->runlist.mem[1]); + if (ret) + return ret; + + init_waitqueue_head(&fifo->runlist.wait); + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 128 * 0x1000, + 0x1000, false, &fifo->user.mem); + if (ret) + return ret; + + ret = nvkm_vmm_get(bar, 12, nvkm_memory_size(fifo->user.mem), + &fifo->user.bar); + if (ret) + return ret; + + return nvkm_memory_map(fifo->user.mem, 0, bar, fifo->user.bar, NULL, 0); +} + +static void +gf100_fifo_fini(struct nvkm_fifo *base) +{ + struct gf100_fifo *fifo = gf100_fifo(base); + flush_work(&fifo->recover.work); +} + +static void +gf100_fifo_init(struct nvkm_fifo *base) +{ + struct gf100_fifo *fifo = gf100_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + int i; + + /* Enable PBDMAs. */ + nvkm_wr32(device, 0x000204, (1 << fifo->pbdma_nr) - 1); + nvkm_wr32(device, 0x002204, (1 << fifo->pbdma_nr) - 1); + + /* Assign engines to PBDMAs. */ + if (fifo->pbdma_nr >= 3) { + nvkm_wr32(device, 0x002208, ~(1 << 0)); /* PGRAPH */ + nvkm_wr32(device, 0x00220c, ~(1 << 1)); /* PVP */ + nvkm_wr32(device, 0x002210, ~(1 << 1)); /* PMSPP */ + nvkm_wr32(device, 0x002214, ~(1 << 1)); /* PMSVLD */ + nvkm_wr32(device, 0x002218, ~(1 << 2)); /* PCE0 */ + nvkm_wr32(device, 0x00221c, ~(1 << 1)); /* PCE1 */ + } + + /* PBDMA[n] */ + for (i = 0; i < fifo->pbdma_nr; i++) { + nvkm_mask(device, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); + nvkm_wr32(device, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ + nvkm_wr32(device, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */ + } + + nvkm_mask(device, 0x002200, 0x00000001, 0x00000001); + nvkm_wr32(device, 0x002254, 0x10000000 | fifo->user.bar->addr >> 12); + + nvkm_wr32(device, 0x002100, 0xffffffff); + nvkm_wr32(device, 0x002140, 0x7fffffff); + nvkm_wr32(device, 0x002628, 0x00000001); /* ENGINE_INTR_EN */ +} + +static void * +gf100_fifo_dtor(struct nvkm_fifo *base) +{ + struct gf100_fifo *fifo = gf100_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + nvkm_vmm_put(nvkm_bar_bar1_vmm(device), &fifo->user.bar); + nvkm_memory_unref(&fifo->user.mem); + nvkm_memory_unref(&fifo->runlist.mem[0]); + nvkm_memory_unref(&fifo->runlist.mem[1]); + return fifo; +} + +static const struct nvkm_fifo_func +gf100_fifo = { + .dtor = gf100_fifo_dtor, + .oneinit = gf100_fifo_oneinit, + .init = gf100_fifo_init, + .fini = gf100_fifo_fini, + .intr = gf100_fifo_intr, + .fault = gf100_fifo_fault, + .engine_id = gf100_fifo_engine_id, + .id_engine = gf100_fifo_id_engine, + .uevent_init = gf100_fifo_uevent_init, + .uevent_fini = gf100_fifo_uevent_fini, + .chan = { + &gf100_fifo_gpfifo_oclass, + NULL + }, +}; + +int +gf100_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + struct gf100_fifo *fifo; + + if (!(fifo = kzalloc(sizeof(*fifo), GFP_KERNEL))) + return -ENOMEM; + INIT_LIST_HEAD(&fifo->chan); + INIT_WORK(&fifo->recover.work, gf100_fifo_recover_work); + *pfifo = &fifo->base; + + return nvkm_fifo_ctor(&gf100_fifo, device, type, inst, 128, &fifo->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.h new file mode 100644 index 000000000..b8642490e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __GF100_FIFO_H__ +#define __GF100_FIFO_H__ +#define gf100_fifo(p) container_of((p), struct gf100_fifo, base) +#include "priv.h" + +#include + +struct gf100_fifo_chan; +struct gf100_fifo { + struct nvkm_fifo base; + + struct list_head chan; + + struct { + struct work_struct work; + u64 mask; + } recover; + + int pbdma_nr; + + struct { + struct nvkm_memory *mem[2]; + int active; + wait_queue_head_t wait; + } runlist; + + struct { + struct nvkm_memory *mem; + struct nvkm_vma *bar; + } user; +}; + +void gf100_fifo_intr_engine(struct gf100_fifo *); +void gf100_fifo_runlist_insert(struct gf100_fifo *, struct gf100_fifo_chan *); +void gf100_fifo_runlist_remove(struct gf100_fifo *, struct gf100_fifo_chan *); +void gf100_fifo_runlist_commit(struct gf100_fifo *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c new file mode 100644 index 000000000..e771bd519 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c @@ -0,0 +1,1249 @@ +/* + * 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 "gk104.h" +#include "cgrp.h" +#include "changk104.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void +gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn, + struct gk104_fifo_engine_status *status) +{ + struct nvkm_engine *engine = fifo->engine[engn].engine; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x002640 + (engn * 0x08)); + + status->busy = !!(stat & 0x80000000); + status->faulted = !!(stat & 0x40000000); + status->next.tsg = !!(stat & 0x10000000); + status->next.id = (stat & 0x0fff0000) >> 16; + status->chsw = !!(stat & 0x00008000); + status->save = !!(stat & 0x00004000); + status->load = !!(stat & 0x00002000); + status->prev.tsg = !!(stat & 0x00001000); + status->prev.id = (stat & 0x00000fff); + status->chan = NULL; + + if (status->busy && status->chsw) { + if (status->load && status->save) { + if (engine && nvkm_engine_chsw_load(engine)) + status->chan = &status->next; + else + status->chan = &status->prev; + } else + if (status->load) { + status->chan = &status->next; + } else { + status->chan = &status->prev; + } + } else + if (status->load) { + status->chan = &status->prev; + } + + nvkm_debug(subdev, "engine %02d: busy %d faulted %d chsw %d " + "save %d load %d %sid %d%s-> %sid %d%s\n", + engn, status->busy, status->faulted, + status->chsw, status->save, status->load, + status->prev.tsg ? "tsg" : "ch", status->prev.id, + status->chan == &status->prev ? "*" : " ", + status->next.tsg ? "tsg" : "ch", status->next.id, + status->chan == &status->next ? "*" : " "); +} + +int +gk104_fifo_class_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *argv, u32 argc, struct nvkm_object **pobject) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + if (oclass->engn == &fifo->func->chan) { + const struct gk104_fifo_chan_user *user = oclass->engn; + return user->ctor(fifo, oclass, argv, argc, pobject); + } else + if (oclass->engn == &fifo->func->user) { + const struct gk104_fifo_user_user *user = oclass->engn; + return user->ctor(oclass, argv, argc, pobject); + } + WARN_ON(1); + return -EINVAL; +} + +int +gk104_fifo_class_get(struct nvkm_fifo *base, int index, + struct nvkm_oclass *oclass) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + int c = 0; + + if (fifo->func->user.ctor && c++ == index) { + oclass->base = fifo->func->user.user; + oclass->engn = &fifo->func->user; + return 0; + } + + if (fifo->func->chan.ctor && c++ == index) { + oclass->base = fifo->func->chan.user; + oclass->engn = &fifo->func->chan; + return 0; + } + + return c; +} + +void +gk104_fifo_uevent_fini(struct nvkm_fifo *fifo) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + nvkm_mask(device, 0x002140, 0x80000000, 0x00000000); +} + +void +gk104_fifo_uevent_init(struct nvkm_fifo *fifo) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + nvkm_mask(device, 0x002140, 0x80000000, 0x80000000); +} + +void +gk104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl, + struct nvkm_memory *mem, int nr) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + int target; + + switch (nvkm_memory_target(mem)) { + case NVKM_MEM_TARGET_VRAM: target = 0; break; + case NVKM_MEM_TARGET_NCOH: target = 3; break; + default: + WARN_ON(1); + return; + } + + nvkm_wr32(device, 0x002270, (nvkm_memory_addr(mem) >> 12) | + (target << 28)); + nvkm_wr32(device, 0x002274, (runl << 20) | nr); + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x002284 + (runl * 0x08)) & 0x00100000)) + break; + ) < 0) + nvkm_error(subdev, "runlist %d update timeout\n", runl); +} + +void +gk104_fifo_runlist_update(struct gk104_fifo *fifo, int runl) +{ + const struct gk104_fifo_runlist_func *func = fifo->func->runlist; + struct gk104_fifo_chan *chan; + struct nvkm_memory *mem; + struct nvkm_fifo_cgrp *cgrp; + int nr = 0; + + mutex_lock(&fifo->base.mutex); + mem = fifo->runlist[runl].mem[fifo->runlist[runl].next]; + fifo->runlist[runl].next = !fifo->runlist[runl].next; + + nvkm_kmap(mem); + list_for_each_entry(chan, &fifo->runlist[runl].chan, head) { + func->chan(chan, mem, nr++ * func->size); + } + + list_for_each_entry(cgrp, &fifo->runlist[runl].cgrp, head) { + func->cgrp(cgrp, mem, nr++ * func->size); + list_for_each_entry(chan, &cgrp->chan, head) { + func->chan(chan, mem, nr++ * func->size); + } + } + nvkm_done(mem); + + func->commit(fifo, runl, mem, nr); + mutex_unlock(&fifo->base.mutex); +} + +void +gk104_fifo_runlist_remove(struct gk104_fifo *fifo, struct gk104_fifo_chan *chan) +{ + struct nvkm_fifo_cgrp *cgrp = chan->cgrp; + mutex_lock(&fifo->base.mutex); + if (!list_empty(&chan->head)) { + list_del_init(&chan->head); + if (cgrp && !--cgrp->chan_nr) + list_del_init(&cgrp->head); + } + mutex_unlock(&fifo->base.mutex); +} + +void +gk104_fifo_runlist_insert(struct gk104_fifo *fifo, struct gk104_fifo_chan *chan) +{ + struct nvkm_fifo_cgrp *cgrp = chan->cgrp; + mutex_lock(&fifo->base.mutex); + if (cgrp) { + if (!cgrp->chan_nr++) + list_add_tail(&cgrp->head, &fifo->runlist[chan->runl].cgrp); + list_add_tail(&chan->head, &cgrp->chan); + } else { + list_add_tail(&chan->head, &fifo->runlist[chan->runl].chan); + } + mutex_unlock(&fifo->base.mutex); +} + +void +gk104_fifo_runlist_chan(struct gk104_fifo_chan *chan, + struct nvkm_memory *memory, u32 offset) +{ + nvkm_wo32(memory, offset + 0, chan->base.chid); + nvkm_wo32(memory, offset + 4, 0x00000000); +} + +const struct gk104_fifo_runlist_func +gk104_fifo_runlist = { + .size = 8, + .chan = gk104_fifo_runlist_chan, + .commit = gk104_fifo_runlist_commit, +}; + +void +gk104_fifo_pbdma_init(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + nvkm_wr32(device, 0x000204, (1 << fifo->pbdma_nr) - 1); +} + +int +gk104_fifo_pbdma_nr(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + /* Determine number of PBDMAs by checking valid enable bits. */ + nvkm_wr32(device, 0x000204, 0xffffffff); + return hweight32(nvkm_rd32(device, 0x000204)); +} + +const struct gk104_fifo_pbdma_func +gk104_fifo_pbdma = { + .nr = gk104_fifo_pbdma_nr, + .init = gk104_fifo_pbdma_init, +}; + +struct nvkm_engine * +gk104_fifo_id_engine(struct nvkm_fifo *base, int engi) +{ + if (engi == GK104_FIFO_ENGN_SW) + return nvkm_device_engine(base->engine.subdev.device, NVKM_ENGINE_SW, 0); + + return gk104_fifo(base)->engine[engi].engine; +} + +int +gk104_fifo_engine_id(struct nvkm_fifo *base, struct nvkm_engine *engine) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + int engn; + + if (engine->subdev.type == NVKM_ENGINE_SW) + return GK104_FIFO_ENGN_SW; + + for (engn = 0; engn < fifo->engine_nr && engine; engn++) { + if (fifo->engine[engn].engine == engine) + return engn; + } + + WARN_ON(1); + return -1; +} + +static void +gk104_fifo_recover_work(struct work_struct *w) +{ + struct gk104_fifo *fifo = container_of(w, typeof(*fifo), recover.work); + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_engine *engine; + unsigned long flags; + u32 engm, runm, todo; + int engn, runl; + + spin_lock_irqsave(&fifo->base.lock, flags); + runm = fifo->recover.runm; + engm = fifo->recover.engm; + fifo->recover.engm = 0; + fifo->recover.runm = 0; + spin_unlock_irqrestore(&fifo->base.lock, flags); + + nvkm_mask(device, 0x002630, runm, runm); + + for (todo = engm; engn = __ffs(todo), todo; todo &= ~BIT(engn)) { + if ((engine = fifo->engine[engn].engine)) { + nvkm_subdev_fini(&engine->subdev, false); + WARN_ON(nvkm_subdev_init(&engine->subdev)); + } + } + + for (todo = runm; runl = __ffs(todo), todo; todo &= ~BIT(runl)) + gk104_fifo_runlist_update(fifo, runl); + + nvkm_wr32(device, 0x00262c, runm); + nvkm_mask(device, 0x002630, runm, 0x00000000); +} + +static void gk104_fifo_recover_engn(struct gk104_fifo *fifo, int engn); + +static void +gk104_fifo_recover_runl(struct gk104_fifo *fifo, int runl) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 runm = BIT(runl); + + assert_spin_locked(&fifo->base.lock); + if (fifo->recover.runm & runm) + return; + fifo->recover.runm |= runm; + + /* Block runlist to prevent channel assignment(s) from changing. */ + nvkm_mask(device, 0x002630, runm, runm); + + /* Schedule recovery. */ + nvkm_warn(subdev, "runlist %d: scheduled for recovery\n", runl); + schedule_work(&fifo->recover.work); +} + +static struct gk104_fifo_chan * +gk104_fifo_recover_chid(struct gk104_fifo *fifo, int runl, int chid) +{ + struct gk104_fifo_chan *chan; + struct nvkm_fifo_cgrp *cgrp; + + list_for_each_entry(chan, &fifo->runlist[runl].chan, head) { + if (chan->base.chid == chid) { + list_del_init(&chan->head); + return chan; + } + } + + list_for_each_entry(cgrp, &fifo->runlist[runl].cgrp, head) { + if (cgrp->id == chid) { + chan = list_first_entry(&cgrp->chan, typeof(*chan), head); + list_del_init(&chan->head); + if (!--cgrp->chan_nr) + list_del_init(&cgrp->head); + return chan; + } + } + + return NULL; +} + +static void +gk104_fifo_recover_chan(struct nvkm_fifo *base, int chid) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 stat = nvkm_rd32(device, 0x800004 + (chid * 0x08)); + const u32 runl = (stat & 0x000f0000) >> 16; + const bool used = (stat & 0x00000001); + unsigned long engn, engm = fifo->runlist[runl].engm; + struct gk104_fifo_chan *chan; + + assert_spin_locked(&fifo->base.lock); + if (!used) + return; + + /* Lookup SW state for channel, and mark it as dead. */ + chan = gk104_fifo_recover_chid(fifo, runl, chid); + if (chan) { + chan->killed = true; + nvkm_fifo_kevent(&fifo->base, chid); + } + + /* Disable channel. */ + nvkm_wr32(device, 0x800004 + (chid * 0x08), stat | 0x00000800); + nvkm_warn(subdev, "channel %d: killed\n", chid); + + /* Block channel assignments from changing during recovery. */ + gk104_fifo_recover_runl(fifo, runl); + + /* Schedule recovery for any engines the channel is on. */ + for_each_set_bit(engn, &engm, fifo->engine_nr) { + struct gk104_fifo_engine_status status; + gk104_fifo_engine_status(fifo, engn, &status); + if (!status.chan || status.chan->id != chid) + continue; + gk104_fifo_recover_engn(fifo, engn); + } +} + +static void +gk104_fifo_recover_engn(struct gk104_fifo *fifo, int engn) +{ + struct nvkm_engine *engine = fifo->engine[engn].engine; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 runl = fifo->engine[engn].runl; + const u32 engm = BIT(engn); + struct gk104_fifo_engine_status status; + int mmui = -1; + + assert_spin_locked(&fifo->base.lock); + if (fifo->recover.engm & engm) + return; + fifo->recover.engm |= engm; + + /* Block channel assignments from changing during recovery. */ + gk104_fifo_recover_runl(fifo, runl); + + /* Determine which channel (if any) is currently on the engine. */ + gk104_fifo_engine_status(fifo, engn, &status); + if (status.chan) { + /* The channel is not longer viable, kill it. */ + gk104_fifo_recover_chan(&fifo->base, status.chan->id); + } + + /* Determine MMU fault ID for the engine, if we're not being + * called from the fault handler already. + */ + if (!status.faulted && engine) { + mmui = nvkm_top_fault_id(device, engine->subdev.type, engine->subdev.inst); + if (mmui < 0) { + const struct nvkm_enum *en = fifo->func->fault.engine; + for (; en && en->name; en++) { + if (en->data2 == engine->subdev.type && + en->inst == engine->subdev.inst) { + mmui = en->value; + break; + } + } + } + WARN_ON(mmui < 0); + } + + /* Trigger a MMU fault for the engine. + * + * No good idea why this is needed, but nvgpu does something similar, + * and it makes recovery from CTXSW_TIMEOUT a lot more reliable. + */ + if (mmui >= 0) { + nvkm_wr32(device, 0x002a30 + (engn * 0x04), 0x00000100 | mmui); + + /* Wait for fault to trigger. */ + nvkm_msec(device, 2000, + gk104_fifo_engine_status(fifo, engn, &status); + if (status.faulted) + break; + ); + + /* Release MMU fault trigger, and ACK the fault. */ + nvkm_wr32(device, 0x002a30 + (engn * 0x04), 0x00000000); + nvkm_wr32(device, 0x00259c, BIT(mmui)); + nvkm_wr32(device, 0x002100, 0x10000000); + } + + /* Schedule recovery. */ + nvkm_warn(subdev, "engine %d: scheduled for recovery\n", engn); + schedule_work(&fifo->recover.work); +} + +static void +gk104_fifo_fault(struct nvkm_fifo *base, struct nvkm_fault_data *info) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const struct nvkm_enum *er, *ee, *ec, *ea; + struct nvkm_engine *engine = NULL; + struct nvkm_fifo_chan *chan; + unsigned long flags; + const char *en = ""; + char ct[8] = "HUB/"; + + er = nvkm_enum_find(fifo->func->fault.reason, info->reason); + ee = nvkm_enum_find(fifo->func->fault.engine, info->engine); + if (info->hub) { + ec = nvkm_enum_find(fifo->func->fault.hubclient, info->client); + } else { + ec = nvkm_enum_find(fifo->func->fault.gpcclient, info->client); + snprintf(ct, sizeof(ct), "GPC%d/", info->gpc); + } + ea = nvkm_enum_find(fifo->func->fault.access, info->access); + + if (ee && ee->data2) { + switch (ee->data2) { + case NVKM_SUBDEV_BAR: + nvkm_bar_bar1_reset(device); + break; + case NVKM_SUBDEV_INSTMEM: + nvkm_bar_bar2_reset(device); + break; + case NVKM_ENGINE_IFB: + nvkm_mask(device, 0x001718, 0x00000000, 0x00000000); + break; + default: + engine = nvkm_device_engine(device, ee->data2, 0); + break; + } + } + + if (ee == NULL) { + struct nvkm_subdev *subdev = nvkm_top_fault(device, info->engine); + if (subdev) { + if (subdev->func == &nvkm_engine) + engine = container_of(subdev, typeof(*engine), subdev); + en = engine->subdev.name; + } + } else { + en = ee->name; + } + + spin_lock_irqsave(&fifo->base.lock, flags); + chan = nvkm_fifo_chan_inst_locked(&fifo->base, info->inst); + + nvkm_error(subdev, + "fault %02x [%s] at %016llx engine %02x [%s] client %02x " + "[%s%s] reason %02x [%s] on channel %d [%010llx %s]\n", + info->access, ea ? ea->name : "", info->addr, + info->engine, ee ? ee->name : en, + info->client, ct, ec ? ec->name : "", + info->reason, er ? er->name : "", chan ? chan->chid : -1, + info->inst, chan ? chan->object.client->name : "unknown"); + + /* Kill the channel that caused the fault. */ + if (chan) + gk104_fifo_recover_chan(&fifo->base, chan->chid); + + /* Channel recovery will probably have already done this for the + * correct engine(s), but just in case we can't find the channel + * information... + */ + if (engine) { + int engn = fifo->base.func->engine_id(&fifo->base, engine); + if (engn >= 0 && engn != GK104_FIFO_ENGN_SW) + gk104_fifo_recover_engn(fifo, engn); + } + + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +static const struct nvkm_enum +gk104_fifo_bind_reason[] = { + { 0x01, "BIND_NOT_UNBOUND" }, + { 0x02, "SNOOP_WITHOUT_BAR1" }, + { 0x03, "UNBIND_WHILE_RUNNING" }, + { 0x05, "INVALID_RUNLIST" }, + { 0x06, "INVALID_CTX_TGT" }, + { 0x0b, "UNBIND_WHILE_PARKED" }, + {} +}; + +void +gk104_fifo_intr_bind(struct gk104_fifo *fifo) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 intr = nvkm_rd32(device, 0x00252c); + u32 code = intr & 0x000000ff; + const struct nvkm_enum *en = + nvkm_enum_find(gk104_fifo_bind_reason, code); + + nvkm_error(subdev, "BIND_ERROR %02x [%s]\n", code, en ? en->name : ""); +} + +static const struct nvkm_enum +gk104_fifo_sched_reason[] = { + { 0x0a, "CTXSW_TIMEOUT" }, + {} +}; + +static void +gk104_fifo_intr_sched_ctxsw(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + unsigned long flags, engm = 0; + u32 engn; + + /* We need to ACK the SCHED_ERROR here, and prevent it reasserting, + * as MMU_FAULT cannot be triggered while it's pending. + */ + spin_lock_irqsave(&fifo->base.lock, flags); + nvkm_mask(device, 0x002140, 0x00000100, 0x00000000); + nvkm_wr32(device, 0x002100, 0x00000100); + + for (engn = 0; engn < fifo->engine_nr; engn++) { + struct gk104_fifo_engine_status status; + + gk104_fifo_engine_status(fifo, engn, &status); + if (!status.busy || !status.chsw) + continue; + + engm |= BIT(engn); + } + + for_each_set_bit(engn, &engm, fifo->engine_nr) + gk104_fifo_recover_engn(fifo, engn); + + nvkm_mask(device, 0x002140, 0x00000100, 0x00000100); + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +static void +gk104_fifo_intr_sched(struct gk104_fifo *fifo) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 intr = nvkm_rd32(device, 0x00254c); + u32 code = intr & 0x000000ff; + const struct nvkm_enum *en = + nvkm_enum_find(gk104_fifo_sched_reason, code); + + nvkm_error(subdev, "SCHED_ERROR %02x [%s]\n", code, en ? en->name : ""); + + switch (code) { + case 0x0a: + gk104_fifo_intr_sched_ctxsw(fifo); + break; + default: + break; + } +} + +void +gk104_fifo_intr_chsw(struct gk104_fifo *fifo) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x00256c); + nvkm_error(subdev, "CHSW_ERROR %08x\n", stat); + nvkm_wr32(device, 0x00256c, stat); +} + +void +gk104_fifo_intr_dropped_fault(struct gk104_fifo *fifo) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x00259c); + nvkm_error(subdev, "DROPPED_MMU_FAULT %08x\n", stat); +} + +static const struct nvkm_bitfield gk104_fifo_pbdma_intr_0[] = { + { 0x00000001, "MEMREQ" }, + { 0x00000002, "MEMACK_TIMEOUT" }, + { 0x00000004, "MEMACK_EXTRA" }, + { 0x00000008, "MEMDAT_TIMEOUT" }, + { 0x00000010, "MEMDAT_EXTRA" }, + { 0x00000020, "MEMFLUSH" }, + { 0x00000040, "MEMOP" }, + { 0x00000080, "LBCONNECT" }, + { 0x00000100, "LBREQ" }, + { 0x00000200, "LBACK_TIMEOUT" }, + { 0x00000400, "LBACK_EXTRA" }, + { 0x00000800, "LBDAT_TIMEOUT" }, + { 0x00001000, "LBDAT_EXTRA" }, + { 0x00002000, "GPFIFO" }, + { 0x00004000, "GPPTR" }, + { 0x00008000, "GPENTRY" }, + { 0x00010000, "GPCRC" }, + { 0x00020000, "PBPTR" }, + { 0x00040000, "PBENTRY" }, + { 0x00080000, "PBCRC" }, + { 0x00100000, "XBARCONNECT" }, + { 0x00200000, "METHOD" }, + { 0x00400000, "METHODCRC" }, + { 0x00800000, "DEVICE" }, + { 0x02000000, "SEMAPHORE" }, + { 0x04000000, "ACQUIRE" }, + { 0x08000000, "PRI" }, + { 0x20000000, "NO_CTXSW_SEG" }, + { 0x40000000, "PBSEG" }, + { 0x80000000, "SIGNATURE" }, + {} +}; + +void +gk104_fifo_intr_pbdma_0(struct gk104_fifo *fifo, int unit) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mask = nvkm_rd32(device, 0x04010c + (unit * 0x2000)); + u32 stat = nvkm_rd32(device, 0x040108 + (unit * 0x2000)) & mask; + u32 addr = nvkm_rd32(device, 0x0400c0 + (unit * 0x2000)); + u32 data = nvkm_rd32(device, 0x0400c4 + (unit * 0x2000)); + u32 chid = nvkm_rd32(device, 0x040120 + (unit * 0x2000)) & 0xfff; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00003ffc); + u32 show = stat; + struct nvkm_fifo_chan *chan; + unsigned long flags; + char msg[128]; + + if (stat & 0x00800000) { + if (device->sw) { + if (nvkm_sw_mthd(device->sw, chid, subc, mthd, data)) + show &= ~0x00800000; + } + } + + nvkm_wr32(device, 0x0400c0 + (unit * 0x2000), 0x80600008); + + if (show) { + nvkm_snprintbf(msg, sizeof(msg), gk104_fifo_pbdma_intr_0, show); + chan = nvkm_fifo_chan_chid(&fifo->base, chid, &flags); + nvkm_error(subdev, "PBDMA%d: %08x [%s] ch %d [%010llx %s] " + "subc %d mthd %04x data %08x\n", + unit, show, msg, chid, chan ? chan->inst->addr : 0, + chan ? chan->object.client->name : "unknown", + subc, mthd, data); + nvkm_fifo_chan_put(&fifo->base, flags, &chan); + } + + nvkm_wr32(device, 0x040108 + (unit * 0x2000), stat); +} + +static const struct nvkm_bitfield gk104_fifo_pbdma_intr_1[] = { + { 0x00000001, "HCE_RE_ILLEGAL_OP" }, + { 0x00000002, "HCE_RE_ALIGNB" }, + { 0x00000004, "HCE_PRIV" }, + { 0x00000008, "HCE_ILLEGAL_MTHD" }, + { 0x00000010, "HCE_ILLEGAL_CLASS" }, + {} +}; + +void +gk104_fifo_intr_pbdma_1(struct gk104_fifo *fifo, int unit) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mask = nvkm_rd32(device, 0x04014c + (unit * 0x2000)); + u32 stat = nvkm_rd32(device, 0x040148 + (unit * 0x2000)) & mask; + u32 chid = nvkm_rd32(device, 0x040120 + (unit * 0x2000)) & 0xfff; + char msg[128]; + + if (stat) { + nvkm_snprintbf(msg, sizeof(msg), gk104_fifo_pbdma_intr_1, stat); + nvkm_error(subdev, "PBDMA%d: %08x [%s] ch %d %08x %08x\n", + unit, stat, msg, chid, + nvkm_rd32(device, 0x040150 + (unit * 0x2000)), + nvkm_rd32(device, 0x040154 + (unit * 0x2000))); + } + + nvkm_wr32(device, 0x040148 + (unit * 0x2000), stat); +} + +void +gk104_fifo_intr_runlist(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 mask = nvkm_rd32(device, 0x002a00); + while (mask) { + int runl = __ffs(mask); + wake_up(&fifo->runlist[runl].wait); + nvkm_wr32(device, 0x002a00, 1 << runl); + mask &= ~(1 << runl); + } +} + +void +gk104_fifo_intr_engine(struct gk104_fifo *fifo) +{ + nvkm_fifo_uevent(&fifo->base); +} + +static void +gk104_fifo_intr(struct nvkm_fifo *base) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mask = nvkm_rd32(device, 0x002140); + u32 stat = nvkm_rd32(device, 0x002100) & mask; + + if (stat & 0x00000001) { + gk104_fifo_intr_bind(fifo); + nvkm_wr32(device, 0x002100, 0x00000001); + stat &= ~0x00000001; + } + + if (stat & 0x00000010) { + nvkm_error(subdev, "PIO_ERROR\n"); + nvkm_wr32(device, 0x002100, 0x00000010); + stat &= ~0x00000010; + } + + if (stat & 0x00000100) { + gk104_fifo_intr_sched(fifo); + nvkm_wr32(device, 0x002100, 0x00000100); + stat &= ~0x00000100; + } + + if (stat & 0x00010000) { + gk104_fifo_intr_chsw(fifo); + nvkm_wr32(device, 0x002100, 0x00010000); + stat &= ~0x00010000; + } + + if (stat & 0x00800000) { + nvkm_error(subdev, "FB_FLUSH_TIMEOUT\n"); + nvkm_wr32(device, 0x002100, 0x00800000); + stat &= ~0x00800000; + } + + if (stat & 0x01000000) { + nvkm_error(subdev, "LB_ERROR\n"); + nvkm_wr32(device, 0x002100, 0x01000000); + stat &= ~0x01000000; + } + + if (stat & 0x08000000) { + gk104_fifo_intr_dropped_fault(fifo); + nvkm_wr32(device, 0x002100, 0x08000000); + stat &= ~0x08000000; + } + + if (stat & 0x10000000) { + u32 mask = nvkm_rd32(device, 0x00259c); + while (mask) { + u32 unit = __ffs(mask); + fifo->func->intr.fault(&fifo->base, unit); + nvkm_wr32(device, 0x00259c, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x10000000; + } + + if (stat & 0x20000000) { + u32 mask = nvkm_rd32(device, 0x0025a0); + while (mask) { + u32 unit = __ffs(mask); + gk104_fifo_intr_pbdma_0(fifo, unit); + gk104_fifo_intr_pbdma_1(fifo, unit); + nvkm_wr32(device, 0x0025a0, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x20000000; + } + + if (stat & 0x40000000) { + gk104_fifo_intr_runlist(fifo); + stat &= ~0x40000000; + } + + if (stat & 0x80000000) { + nvkm_wr32(device, 0x002100, 0x80000000); + gk104_fifo_intr_engine(fifo); + stat &= ~0x80000000; + } + + if (stat) { + nvkm_error(subdev, "INTR %08x\n", stat); + nvkm_mask(device, 0x002140, stat, 0x00000000); + nvkm_wr32(device, 0x002100, stat); + } +} + +void +gk104_fifo_fini(struct nvkm_fifo *base) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + flush_work(&fifo->recover.work); + /* allow mmu fault interrupts, even when we're not using fifo */ + nvkm_mask(device, 0x002140, 0x10000000, 0x10000000); +} + +int +gk104_fifo_info(struct nvkm_fifo *base, u64 mthd, u64 *data) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + switch (mthd) { + case NV_DEVICE_HOST_RUNLISTS: + *data = (1ULL << fifo->runlist_nr) - 1; + return 0; + case NV_DEVICE_HOST_RUNLIST_ENGINES: { + if (*data < fifo->runlist_nr) { + unsigned long engm = fifo->runlist[*data].engm; + struct nvkm_engine *engine; + int engn; + *data = 0; + for_each_set_bit(engn, &engm, fifo->engine_nr) { + if ((engine = fifo->engine[engn].engine)) { +#define CASE(n) case NVKM_ENGINE_##n: *data |= NV_DEVICE_HOST_RUNLIST_ENGINES_##n; break + switch (engine->subdev.type) { + CASE(SW ); + CASE(GR ); + CASE(MPEG ); + CASE(ME ); + CASE(CIPHER); + CASE(BSP ); + CASE(VP ); + CASE(CE ); + CASE(SEC ); + CASE(MSVLD ); + CASE(MSPDEC); + CASE(MSPPP ); + CASE(MSENC ); + CASE(VIC ); + CASE(SEC2 ); + CASE(NVDEC ); + CASE(NVENC ); + default: + WARN_ON(1); + break; + } + } + } + return 0; + } + } + return -EINVAL; + default: + return -EINVAL; + } +} + +int +gk104_fifo_oneinit(struct nvkm_fifo *base) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_vmm *bar = nvkm_bar_bar1_vmm(device); + struct nvkm_top_device *tdev; + int pbid, ret, i, j; + u32 *map; + + fifo->pbdma_nr = fifo->func->pbdma->nr(fifo); + nvkm_debug(subdev, "%d PBDMA(s)\n", fifo->pbdma_nr); + + /* Read PBDMA->runlist(s) mapping from HW. */ + if (!(map = kcalloc(fifo->pbdma_nr, sizeof(*map), GFP_KERNEL))) + return -ENOMEM; + + for (i = 0; i < fifo->pbdma_nr; i++) + map[i] = nvkm_rd32(device, 0x002390 + (i * 0x04)); + + /* Determine runlist configuration from topology device info. */ + list_for_each_entry(tdev, &device->top->device, head) { + const int engn = tdev->engine; + char _en[16], *en; + + if (engn < 0) + continue; + + /* Determine which PBDMA handles requests for this engine. */ + for (j = 0, pbid = -1; j < fifo->pbdma_nr; j++) { + if (map[j] & BIT(tdev->runlist)) { + pbid = j; + break; + } + } + + fifo->engine[engn].engine = nvkm_device_engine(device, tdev->type, tdev->inst); + if (!fifo->engine[engn].engine) { + snprintf(_en, sizeof(_en), "%s, %d", + nvkm_subdev_type[tdev->type], tdev->inst); + en = _en; + } else { + en = fifo->engine[engn].engine->subdev.name; + } + + nvkm_debug(subdev, "engine %2d: runlist %2d pbdma %2d (%s)\n", + tdev->engine, tdev->runlist, pbid, en); + + fifo->engine[engn].runl = tdev->runlist; + fifo->engine[engn].pbid = pbid; + fifo->engine_nr = max(fifo->engine_nr, engn + 1); + fifo->runlist[tdev->runlist].engm |= BIT(engn); + fifo->runlist[tdev->runlist].engm_sw |= BIT(engn); + if (tdev->type == NVKM_ENGINE_GR) + fifo->runlist[tdev->runlist].engm_sw |= BIT(GK104_FIFO_ENGN_SW); + fifo->runlist_nr = max(fifo->runlist_nr, tdev->runlist + 1); + } + + kfree(map); + + for (i = 0; i < fifo->runlist_nr; i++) { + for (j = 0; j < ARRAY_SIZE(fifo->runlist[i].mem); j++) { + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, + fifo->base.nr * 2/* TSG+chan */ * + fifo->func->runlist->size, + 0x1000, false, + &fifo->runlist[i].mem[j]); + if (ret) + return ret; + } + + init_waitqueue_head(&fifo->runlist[i].wait); + INIT_LIST_HEAD(&fifo->runlist[i].cgrp); + INIT_LIST_HEAD(&fifo->runlist[i].chan); + } + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, + fifo->base.nr * 0x200, 0x1000, true, + &fifo->user.mem); + if (ret) + return ret; + + ret = nvkm_vmm_get(bar, 12, nvkm_memory_size(fifo->user.mem), + &fifo->user.bar); + if (ret) + return ret; + + return nvkm_memory_map(fifo->user.mem, 0, bar, fifo->user.bar, NULL, 0); +} + +void +gk104_fifo_init(struct nvkm_fifo *base) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + int i; + + /* Enable PBDMAs. */ + fifo->func->pbdma->init(fifo); + + /* PBDMA[n] */ + for (i = 0; i < fifo->pbdma_nr; i++) { + nvkm_mask(device, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); + nvkm_wr32(device, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ + nvkm_wr32(device, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */ + } + + /* PBDMA[n].HCE */ + for (i = 0; i < fifo->pbdma_nr; i++) { + nvkm_wr32(device, 0x040148 + (i * 0x2000), 0xffffffff); /* INTR */ + nvkm_wr32(device, 0x04014c + (i * 0x2000), 0xffffffff); /* INTREN */ + } + + nvkm_wr32(device, 0x002254, 0x10000000 | fifo->user.bar->addr >> 12); + + if (fifo->func->pbdma->init_timeout) + fifo->func->pbdma->init_timeout(fifo); + + nvkm_wr32(device, 0x002100, 0xffffffff); + nvkm_wr32(device, 0x002140, 0x7fffffff); +} + +void * +gk104_fifo_dtor(struct nvkm_fifo *base) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + int i; + + nvkm_vmm_put(nvkm_bar_bar1_vmm(device), &fifo->user.bar); + nvkm_memory_unref(&fifo->user.mem); + + for (i = 0; i < fifo->runlist_nr; i++) { + nvkm_memory_unref(&fifo->runlist[i].mem[1]); + nvkm_memory_unref(&fifo->runlist[i].mem[0]); + } + + return fifo; +} + +static const struct nvkm_fifo_func +gk104_fifo_ = { + .dtor = gk104_fifo_dtor, + .oneinit = gk104_fifo_oneinit, + .info = gk104_fifo_info, + .init = gk104_fifo_init, + .fini = gk104_fifo_fini, + .intr = gk104_fifo_intr, + .fault = gk104_fifo_fault, + .engine_id = gk104_fifo_engine_id, + .id_engine = gk104_fifo_id_engine, + .uevent_init = gk104_fifo_uevent_init, + .uevent_fini = gk104_fifo_uevent_fini, + .recover_chan = gk104_fifo_recover_chan, + .class_get = gk104_fifo_class_get, + .class_new = gk104_fifo_class_new, +}; + +int +gk104_fifo_new_(const struct gk104_fifo_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, int nr, struct nvkm_fifo **pfifo) +{ + struct gk104_fifo *fifo; + + if (!(fifo = kzalloc(sizeof(*fifo), GFP_KERNEL))) + return -ENOMEM; + fifo->func = func; + INIT_WORK(&fifo->recover.work, gk104_fifo_recover_work); + *pfifo = &fifo->base; + + return nvkm_fifo_ctor(&gk104_fifo_, device, type, inst, nr, &fifo->base); +} + +const struct nvkm_enum +gk104_fifo_fault_access[] = { + { 0x0, "READ" }, + { 0x1, "WRITE" }, + {} +}; + +const struct nvkm_enum +gk104_fifo_fault_engine[] = { + { 0x00, "GR", NULL, NVKM_ENGINE_GR }, + { 0x01, "DISPLAY" }, + { 0x02, "CAPTURE" }, + { 0x03, "IFB", NULL, NVKM_ENGINE_IFB }, + { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, + { 0x05, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, + { 0x06, "SCHED" }, + { 0x07, "HOST0", NULL, NVKM_ENGINE_FIFO }, + { 0x08, "HOST1", NULL, NVKM_ENGINE_FIFO }, + { 0x09, "HOST2", NULL, NVKM_ENGINE_FIFO }, + { 0x0a, "HOST3", NULL, NVKM_ENGINE_FIFO }, + { 0x0b, "HOST4", NULL, NVKM_ENGINE_FIFO }, + { 0x0c, "HOST5", NULL, NVKM_ENGINE_FIFO }, + { 0x0d, "HOST6", NULL, NVKM_ENGINE_FIFO }, + { 0x0e, "HOST7", NULL, NVKM_ENGINE_FIFO }, + { 0x0f, "HOSTSR" }, + { 0x10, "MSVLD", NULL, NVKM_ENGINE_MSVLD }, + { 0x11, "MSPPP", NULL, NVKM_ENGINE_MSPPP }, + { 0x13, "PERF" }, + { 0x14, "MSPDEC", NULL, NVKM_ENGINE_MSPDEC }, + { 0x15, "CE0", NULL, NVKM_ENGINE_CE, 0 }, + { 0x16, "CE1", NULL, NVKM_ENGINE_CE, 1 }, + { 0x17, "PMU" }, + { 0x18, "PTP" }, + { 0x19, "MSENC", NULL, NVKM_ENGINE_MSENC }, + { 0x1b, "CE2", NULL, NVKM_ENGINE_CE, 2 }, + {} +}; + +const struct nvkm_enum +gk104_fifo_fault_reason[] = { + { 0x00, "PDE" }, + { 0x01, "PDE_SIZE" }, + { 0x02, "PTE" }, + { 0x03, "VA_LIMIT_VIOLATION" }, + { 0x04, "UNBOUND_INST_BLOCK" }, + { 0x05, "PRIV_VIOLATION" }, + { 0x06, "RO_VIOLATION" }, + { 0x07, "WO_VIOLATION" }, + { 0x08, "PITCH_MASK_VIOLATION" }, + { 0x09, "WORK_CREATION" }, + { 0x0a, "UNSUPPORTED_APERTURE" }, + { 0x0b, "COMPRESSION_FAILURE" }, + { 0x0c, "UNSUPPORTED_KIND" }, + { 0x0d, "REGION_VIOLATION" }, + { 0x0e, "BOTH_PTES_VALID" }, + { 0x0f, "INFO_TYPE_POISONED" }, + {} +}; + +const struct nvkm_enum +gk104_fifo_fault_hubclient[] = { + { 0x00, "VIP" }, + { 0x01, "CE0" }, + { 0x02, "CE1" }, + { 0x03, "DNISO" }, + { 0x04, "FE" }, + { 0x05, "FECS" }, + { 0x06, "HOST" }, + { 0x07, "HOST_CPU" }, + { 0x08, "HOST_CPU_NB" }, + { 0x09, "ISO" }, + { 0x0a, "MMU" }, + { 0x0b, "MSPDEC" }, + { 0x0c, "MSPPP" }, + { 0x0d, "MSVLD" }, + { 0x0e, "NISO" }, + { 0x0f, "P2P" }, + { 0x10, "PD" }, + { 0x11, "PERF" }, + { 0x12, "PMU" }, + { 0x13, "RASTERTWOD" }, + { 0x14, "SCC" }, + { 0x15, "SCC_NB" }, + { 0x16, "SEC" }, + { 0x17, "SSYNC" }, + { 0x18, "GR_CE" }, + { 0x19, "CE2" }, + { 0x1a, "XV" }, + { 0x1b, "MMU_NB" }, + { 0x1c, "MSENC" }, + { 0x1d, "DFALCON" }, + { 0x1e, "SKED" }, + { 0x1f, "AFALCON" }, + {} +}; + +const struct nvkm_enum +gk104_fifo_fault_gpcclient[] = { + { 0x00, "L1_0" }, { 0x01, "T1_0" }, { 0x02, "PE_0" }, + { 0x03, "L1_1" }, { 0x04, "T1_1" }, { 0x05, "PE_1" }, + { 0x06, "L1_2" }, { 0x07, "T1_2" }, { 0x08, "PE_2" }, + { 0x09, "L1_3" }, { 0x0a, "T1_3" }, { 0x0b, "PE_3" }, + { 0x0c, "RAST" }, + { 0x0d, "GCC" }, + { 0x0e, "GPCCS" }, + { 0x0f, "PROP_0" }, + { 0x10, "PROP_1" }, + { 0x11, "PROP_2" }, + { 0x12, "PROP_3" }, + { 0x13, "L1_4" }, { 0x14, "T1_4" }, { 0x15, "PE_4" }, + { 0x16, "L1_5" }, { 0x17, "T1_5" }, { 0x18, "PE_5" }, + { 0x19, "L1_6" }, { 0x1a, "T1_6" }, { 0x1b, "PE_6" }, + { 0x1c, "L1_7" }, { 0x1d, "T1_7" }, { 0x1e, "PE_7" }, + { 0x1f, "GPM" }, + { 0x20, "LTP_UTLB_0" }, + { 0x21, "LTP_UTLB_1" }, + { 0x22, "LTP_UTLB_2" }, + { 0x23, "LTP_UTLB_3" }, + { 0x24, "GPC_RGG_UTLB" }, + {} +}; + +static const struct gk104_fifo_func +gk104_fifo = { + .intr.fault = gf100_fifo_intr_fault, + .pbdma = &gk104_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gk104_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gk104_fifo_runlist, + .chan = {{0,0,KEPLER_CHANNEL_GPFIFO_A}, gk104_fifo_gpfifo_new }, +}; + +int +gk104_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gk104_fifo, device, type, inst, 4096, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h new file mode 100644 index 000000000..f2d12ae73 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __GK104_FIFO_H__ +#define __GK104_FIFO_H__ +#define gk104_fifo(p) container_of((p), struct gk104_fifo, base) +#include "priv.h" +struct nvkm_fifo_cgrp; + +#include +#include + +struct gk104_fifo_chan; +struct gk104_fifo { + const struct gk104_fifo_func *func; + struct nvkm_fifo base; + + struct { + struct work_struct work; + u32 engm; + u32 runm; + } recover; + + int pbdma_nr; + + struct { + struct nvkm_engine *engine; + int runl; + int pbid; + } engine[16]; + int engine_nr; + + struct { + struct nvkm_memory *mem[2]; + int next; + wait_queue_head_t wait; + struct list_head cgrp; + struct list_head chan; + u32 engm; + u32 engm_sw; + } runlist[16]; + int runlist_nr; + + struct { + struct nvkm_memory *mem; + struct nvkm_vma *bar; + } user; +}; + +struct gk104_fifo_func { + struct { + void (*fault)(struct nvkm_fifo *, int unit); + } intr; + + const struct gk104_fifo_pbdma_func { + int (*nr)(struct gk104_fifo *); + void (*init)(struct gk104_fifo *); + void (*init_timeout)(struct gk104_fifo *); + } *pbdma; + + struct { + const struct nvkm_enum *access; + const struct nvkm_enum *engine; + const struct nvkm_enum *reason; + const struct nvkm_enum *hubclient; + const struct nvkm_enum *gpcclient; + } fault; + + const struct gk104_fifo_runlist_func { + u8 size; + void (*cgrp)(struct nvkm_fifo_cgrp *, + struct nvkm_memory *, u32 offset); + void (*chan)(struct gk104_fifo_chan *, + struct nvkm_memory *, u32 offset); + void (*commit)(struct gk104_fifo *, int runl, + struct nvkm_memory *, int entries); + } *runlist; + + struct gk104_fifo_user_user { + struct nvkm_sclass user; + int (*ctor)(const struct nvkm_oclass *, void *, u32, + struct nvkm_object **); + } user; + + struct gk104_fifo_chan_user { + struct nvkm_sclass user; + int (*ctor)(struct gk104_fifo *, const struct nvkm_oclass *, + void *, u32, struct nvkm_object **); + } chan; + bool cgrp_force; +}; + +struct gk104_fifo_engine_status { + bool busy; + bool faulted; + bool chsw; + bool save; + bool load; + struct { + bool tsg; + u32 id; + } prev, next, *chan; +}; + +int gk104_fifo_new_(const struct gk104_fifo_func *, struct nvkm_device *, enum nvkm_subdev_type, + int index, int nr, struct nvkm_fifo **); +void gk104_fifo_runlist_insert(struct gk104_fifo *, struct gk104_fifo_chan *); +void gk104_fifo_runlist_remove(struct gk104_fifo *, struct gk104_fifo_chan *); +void gk104_fifo_runlist_update(struct gk104_fifo *, int runl); +void gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn, + struct gk104_fifo_engine_status *status); +void gk104_fifo_intr_bind(struct gk104_fifo *fifo); +void gk104_fifo_intr_chsw(struct gk104_fifo *fifo); +void gk104_fifo_intr_dropped_fault(struct gk104_fifo *fifo); +void gk104_fifo_intr_pbdma_0(struct gk104_fifo *fifo, int unit); +void gk104_fifo_intr_pbdma_1(struct gk104_fifo *fifo, int unit); +void gk104_fifo_intr_runlist(struct gk104_fifo *fifo); +void gk104_fifo_intr_engine(struct gk104_fifo *fifo); +void *gk104_fifo_dtor(struct nvkm_fifo *base); +int gk104_fifo_oneinit(struct nvkm_fifo *base); +int gk104_fifo_info(struct nvkm_fifo *base, u64 mthd, u64 *data); +void gk104_fifo_init(struct nvkm_fifo *base); +void gk104_fifo_fini(struct nvkm_fifo *base); +int gk104_fifo_class_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *argv, u32 argc, struct nvkm_object **pobject); +int gk104_fifo_class_get(struct nvkm_fifo *base, int index, + struct nvkm_oclass *oclass); +void gk104_fifo_uevent_fini(struct nvkm_fifo *fifo); +void gk104_fifo_uevent_init(struct nvkm_fifo *fifo); + +extern const struct gk104_fifo_pbdma_func gk104_fifo_pbdma; +int gk104_fifo_pbdma_nr(struct gk104_fifo *); +void gk104_fifo_pbdma_init(struct gk104_fifo *); +extern const struct nvkm_enum gk104_fifo_fault_access[]; +extern const struct nvkm_enum gk104_fifo_fault_engine[]; +extern const struct nvkm_enum gk104_fifo_fault_reason[]; +extern const struct nvkm_enum gk104_fifo_fault_hubclient[]; +extern const struct nvkm_enum gk104_fifo_fault_gpcclient[]; +extern const struct gk104_fifo_runlist_func gk104_fifo_runlist; +void gk104_fifo_runlist_chan(struct gk104_fifo_chan *, + struct nvkm_memory *, u32); +void gk104_fifo_runlist_commit(struct gk104_fifo *, int runl, + struct nvkm_memory *, int); + +extern const struct gk104_fifo_runlist_func gk110_fifo_runlist; +void gk110_fifo_runlist_cgrp(struct nvkm_fifo_cgrp *, + struct nvkm_memory *, u32); + +extern const struct gk104_fifo_pbdma_func gk208_fifo_pbdma; +void gk208_fifo_pbdma_init_timeout(struct gk104_fifo *); + +void gm107_fifo_intr_fault(struct nvkm_fifo *, int); +extern const struct nvkm_enum gm107_fifo_fault_engine[]; +extern const struct gk104_fifo_runlist_func gm107_fifo_runlist; + +extern const struct gk104_fifo_pbdma_func gm200_fifo_pbdma; +int gm200_fifo_pbdma_nr(struct gk104_fifo *); + +void gp100_fifo_intr_fault(struct nvkm_fifo *, int); +extern const struct nvkm_enum gp100_fifo_fault_engine[]; + +extern const struct nvkm_enum gv100_fifo_fault_access[]; +extern const struct nvkm_enum gv100_fifo_fault_reason[]; +extern const struct nvkm_enum gv100_fifo_fault_hubclient[]; +extern const struct nvkm_enum gv100_fifo_fault_gpcclient[]; +void gv100_fifo_runlist_cgrp(struct nvkm_fifo_cgrp *, + struct nvkm_memory *, u32); +void gv100_fifo_runlist_chan(struct gk104_fifo_chan *, + struct nvkm_memory *, u32); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c new file mode 100644 index 000000000..915278c7e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c @@ -0,0 +1,67 @@ +/* + * Copyright 2016 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 "gk104.h" +#include "cgrp.h" +#include "changk104.h" + +#include + +#include + +void +gk110_fifo_runlist_cgrp(struct nvkm_fifo_cgrp *cgrp, + struct nvkm_memory *memory, u32 offset) +{ + nvkm_wo32(memory, offset + 0, (cgrp->chan_nr << 26) | (128 << 18) | + (3 << 14) | 0x00002000 | cgrp->id); + nvkm_wo32(memory, offset + 4, 0x00000000); +} + +const struct gk104_fifo_runlist_func +gk110_fifo_runlist = { + .size = 8, + .cgrp = gk110_fifo_runlist_cgrp, + .chan = gk104_fifo_runlist_chan, + .commit = gk104_fifo_runlist_commit, +}; + +static const struct gk104_fifo_func +gk110_fifo = { + .intr.fault = gf100_fifo_intr_fault, + .pbdma = &gk104_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gk104_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gk110_fifo_runlist, + .chan = {{0,0,KEPLER_CHANNEL_GPFIFO_B}, gk104_fifo_gpfifo_new }, +}; + +int +gk110_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gk110_fifo, device, type, inst, 4096, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c new file mode 100644 index 000000000..cb703693d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c @@ -0,0 +1,64 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gk104.h" +#include "changk104.h" + +#include + +void +gk208_fifo_pbdma_init_timeout(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + int i; + + for (i = 0; i < fifo->pbdma_nr; i++) + nvkm_wr32(device, 0x04012c + (i * 0x2000), 0x0000ffff); +} + +const struct gk104_fifo_pbdma_func +gk208_fifo_pbdma = { + .nr = gk104_fifo_pbdma_nr, + .init = gk104_fifo_pbdma_init, + .init_timeout = gk208_fifo_pbdma_init_timeout, +}; + +static const struct gk104_fifo_func +gk208_fifo = { + .intr.fault = gf100_fifo_intr_fault, + .pbdma = &gk208_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gk104_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gk110_fifo_runlist, + .chan = {{0,0,KEPLER_CHANNEL_GPFIFO_A}, gk104_fifo_gpfifo_new }, +}; + +int +gk208_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gk208_fifo, device, type, inst, 1024, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c new file mode 100644 index 000000000..6e35cf44c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gk104.h" +#include "changk104.h" + +#include + +static const struct gk104_fifo_func +gk20a_fifo = { + .intr.fault = gf100_fifo_intr_fault, + .pbdma = &gk208_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gk104_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gk110_fifo_runlist, + .chan = {{0,0,KEPLER_CHANNEL_GPFIFO_A}, gk104_fifo_gpfifo_new }, +}; + +int +gk20a_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gk20a_fifo, device, type, inst, 128, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c new file mode 100644 index 000000000..7af6e687d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c @@ -0,0 +1,113 @@ +/* + * Copyright 2016 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 "gk104.h" +#include "changk104.h" + +#include +#include + +#include + +static void +gm107_fifo_runlist_chan(struct gk104_fifo_chan *chan, + struct nvkm_memory *memory, u32 offset) +{ + nvkm_wo32(memory, offset + 0, chan->base.chid); + nvkm_wo32(memory, offset + 4, chan->base.inst->addr >> 12); +} + +const struct gk104_fifo_runlist_func +gm107_fifo_runlist = { + .size = 8, + .cgrp = gk110_fifo_runlist_cgrp, + .chan = gm107_fifo_runlist_chan, + .commit = gk104_fifo_runlist_commit, +}; + +const struct nvkm_enum +gm107_fifo_fault_engine[] = { + { 0x01, "DISPLAY" }, + { 0x02, "CAPTURE" }, + { 0x03, "IFB", NULL, NVKM_ENGINE_IFB }, + { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, + { 0x05, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, + { 0x06, "SCHED" }, + { 0x07, "HOST0", NULL, NVKM_ENGINE_FIFO }, + { 0x08, "HOST1", NULL, NVKM_ENGINE_FIFO }, + { 0x09, "HOST2", NULL, NVKM_ENGINE_FIFO }, + { 0x0a, "HOST3", NULL, NVKM_ENGINE_FIFO }, + { 0x0b, "HOST4", NULL, NVKM_ENGINE_FIFO }, + { 0x0c, "HOST5", NULL, NVKM_ENGINE_FIFO }, + { 0x0d, "HOST6", NULL, NVKM_ENGINE_FIFO }, + { 0x0e, "HOST7", NULL, NVKM_ENGINE_FIFO }, + { 0x0f, "HOSTSR" }, + { 0x13, "PERF" }, + { 0x17, "PMU" }, + { 0x18, "PTP" }, + {} +}; + +void +gm107_fifo_intr_fault(struct nvkm_fifo *fifo, int unit) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10)); + u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10)); + u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10)); + u32 type = nvkm_rd32(device, 0x00280c + (unit * 0x10)); + struct nvkm_fault_data info; + + info.inst = (u64)inst << 12; + info.addr = ((u64)vahi << 32) | valo; + info.time = 0; + info.engine = unit; + info.valid = 1; + info.gpc = (type & 0x1f000000) >> 24; + info.client = (type & 0x00003f00) >> 8; + info.access = (type & 0x00000080) >> 7; + info.hub = (type & 0x00000040) >> 6; + info.reason = (type & 0x0000000f); + + nvkm_fifo_fault(fifo, &info); +} + +static const struct gk104_fifo_func +gm107_fifo = { + .intr.fault = gm107_fifo_intr_fault, + .pbdma = &gk208_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gm107_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gm107_fifo_runlist, + .chan = {{0,0,KEPLER_CHANNEL_GPFIFO_B}, gk104_fifo_gpfifo_new }, +}; + +int +gm107_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gm107_fifo, device, type, inst, 2048, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c new file mode 100644 index 000000000..573658cb6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c @@ -0,0 +1,61 @@ +/* + * Copyright 2015 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 "gk104.h" +#include "changk104.h" + +#include + +int +gm200_fifo_pbdma_nr(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + return nvkm_rd32(device, 0x002004) & 0x000000ff; +} + +const struct gk104_fifo_pbdma_func +gm200_fifo_pbdma = { + .nr = gm200_fifo_pbdma_nr, + .init = gk104_fifo_pbdma_init, + .init_timeout = gk208_fifo_pbdma_init_timeout, +}; + +static const struct gk104_fifo_func +gm200_fifo = { + .intr.fault = gm107_fifo_intr_fault, + .pbdma = &gm200_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gm107_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gm107_fifo_runlist, + .chan = {{0,0,MAXWELL_CHANNEL_GPFIFO_A}, gk104_fifo_gpfifo_new }, +}; + +int +gm200_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gm200_fifo, device, type, inst, 4096, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c new file mode 100644 index 000000000..556c97e54 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gk104.h" +#include "changk104.h" + +#include + +static const struct gk104_fifo_func +gm20b_fifo = { + .intr.fault = gm107_fifo_intr_fault, + .pbdma = &gm200_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gm107_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gm107_fifo_runlist, + .chan = {{0,0,MAXWELL_CHANNEL_GPFIFO_A}, gk104_fifo_gpfifo_new }, +}; + +int +gm20b_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gm20b_fifo, device, type, inst, 512, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c new file mode 100644 index 000000000..6b46b6b65 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c @@ -0,0 +1,98 @@ +/* + * Copyright 2016 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 "gk104.h" +#include "changk104.h" + +#include + +#include + +const struct nvkm_enum +gp100_fifo_fault_engine[] = { + { 0x01, "DISPLAY" }, + { 0x03, "IFB", NULL, NVKM_ENGINE_IFB }, + { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, + { 0x05, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, + { 0x06, "HOST0", NULL, NVKM_ENGINE_FIFO }, + { 0x07, "HOST1", NULL, NVKM_ENGINE_FIFO }, + { 0x08, "HOST2", NULL, NVKM_ENGINE_FIFO }, + { 0x09, "HOST3", NULL, NVKM_ENGINE_FIFO }, + { 0x0a, "HOST4", NULL, NVKM_ENGINE_FIFO }, + { 0x0b, "HOST5", NULL, NVKM_ENGINE_FIFO }, + { 0x0c, "HOST6", NULL, NVKM_ENGINE_FIFO }, + { 0x0d, "HOST7", NULL, NVKM_ENGINE_FIFO }, + { 0x0e, "HOST8", NULL, NVKM_ENGINE_FIFO }, + { 0x0f, "HOST9", NULL, NVKM_ENGINE_FIFO }, + { 0x10, "HOST10", NULL, NVKM_ENGINE_FIFO }, + { 0x13, "PERF" }, + { 0x17, "PMU" }, + { 0x18, "PTP" }, + { 0x1f, "PHYSICAL" }, + {} +}; + +void +gp100_fifo_intr_fault(struct nvkm_fifo *fifo, int unit) +{ + struct nvkm_device *device = fifo->engine.subdev.device; + u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10)); + u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10)); + u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10)); + u32 type = nvkm_rd32(device, 0x00280c + (unit * 0x10)); + struct nvkm_fault_data info; + + info.inst = (u64)inst << 12; + info.addr = ((u64)vahi << 32) | valo; + info.time = 0; + info.engine = unit; + info.valid = 1; + info.gpc = (type & 0x1f000000) >> 24; + info.hub = (type & 0x00100000) >> 20; + info.access = (type & 0x00070000) >> 16; + info.client = (type & 0x00007f00) >> 8; + info.reason = (type & 0x0000001f); + + nvkm_fifo_fault(fifo, &info); +} + +static const struct gk104_fifo_func +gp100_fifo = { + .intr.fault = gp100_fifo_intr_fault, + .pbdma = &gm200_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gp100_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gm107_fifo_runlist, + .chan = {{0,0,PASCAL_CHANNEL_GPFIFO_A}, gk104_fifo_gpfifo_new }, + .cgrp_force = true, +}; + +int +gp100_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gp100_fifo, device, type, inst, 4096, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c new file mode 100644 index 000000000..7a5929cb4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gk104.h" +#include "changk104.h" + +#include + +static const struct gk104_fifo_func +gp10b_fifo = { + .intr.fault = gp100_fifo_intr_fault, + .pbdma = &gm200_fifo_pbdma, + .fault.access = gk104_fifo_fault_access, + .fault.engine = gp100_fifo_fault_engine, + .fault.reason = gk104_fifo_fault_reason, + .fault.hubclient = gk104_fifo_fault_hubclient, + .fault.gpcclient = gk104_fifo_fault_gpcclient, + .runlist = &gm107_fifo_runlist, + .chan = {{0,0,PASCAL_CHANNEL_GPFIFO_A}, gk104_fifo_gpfifo_new }, + .cgrp_force = true, +}; + +int +gp10b_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gp10b_fifo, device, type, inst, 512, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifog84.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifog84.c new file mode 100644 index 000000000..2121f517b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifog84.c @@ -0,0 +1,95 @@ +/* + * 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 "channv50.h" + +#include +#include + +#include +#include +#include + +static int +g84_fifo_gpfifo_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct g82_channel_gpfifo_v0 v0; + } *args = data; + struct nv50_fifo *fifo = nv50_fifo(base); + struct nv50_fifo_chan *chan; + u64 ioffset, ilength; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel gpfifo size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel gpfifo vers %d vmm %llx " + "pushbuf %llx ioffset %016llx " + "ilength %08x\n", + args->v0.version, args->v0.vmm, args->v0.pushbuf, + args->v0.ioffset, args->v0.ilength); + if (!args->v0.pushbuf) + return -EINVAL; + } else + return ret; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = g84_fifo_chan_ctor(fifo, args->v0.vmm, args->v0.pushbuf, + oclass, chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + ioffset = args->v0.ioffset; + ilength = order_base_2(args->v0.ilength / 8); + + nvkm_kmap(chan->ramfc); + nvkm_wo32(chan->ramfc, 0x3c, 0x403f6078); + nvkm_wo32(chan->ramfc, 0x44, 0x01003fff); + nvkm_wo32(chan->ramfc, 0x48, chan->base.push->node->offset >> 4); + nvkm_wo32(chan->ramfc, 0x50, lower_32_bits(ioffset)); + nvkm_wo32(chan->ramfc, 0x54, upper_32_bits(ioffset) | (ilength << 16)); + nvkm_wo32(chan->ramfc, 0x60, 0x7fffffff); + nvkm_wo32(chan->ramfc, 0x78, 0x00000000); + nvkm_wo32(chan->ramfc, 0x7c, 0x30000001); + nvkm_wo32(chan->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj->node->offset >> 4)); + nvkm_wo32(chan->ramfc, 0x88, chan->cache->addr >> 10); + nvkm_wo32(chan->ramfc, 0x98, chan->base.inst->addr >> 12); + nvkm_done(chan->ramfc); + return 0; +} + +const struct nvkm_fifo_chan_oclass +g84_fifo_gpfifo_oclass = { + .base.oclass = G82_CHANNEL_GPFIFO, + .base.minver = 0, + .base.maxver = 0, + .ctor = g84_fifo_gpfifo_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c new file mode 100644 index 000000000..4e78bbe3b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c @@ -0,0 +1,308 @@ +/* + * 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 "changf100.h" + +#include +#include +#include +#include + +#include +#include +#include + +int +gf100_fifo_chan_ntfy(struct nvkm_fifo_chan *chan, u32 type, + struct nvkm_event **pevent) +{ + switch (type) { + case NV906F_V0_NTFY_NON_STALL_INTERRUPT: + *pevent = &chan->fifo->uevent; + return 0; + case NV906F_V0_NTFY_KILLED: + *pevent = &chan->fifo->kevent; + return 0; + default: + break; + } + return -EINVAL; +} + +static u32 +gf100_fifo_gpfifo_engine_addr(struct nvkm_engine *engine) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_SW : return 0; + case NVKM_ENGINE_GR : return 0x0210; + case NVKM_ENGINE_CE : return 0x0230 + (engine->subdev.inst * 0x10); + case NVKM_ENGINE_MSPDEC: return 0x0250; + case NVKM_ENGINE_MSPPP : return 0x0260; + case NVKM_ENGINE_MSVLD : return 0x0270; + default: + WARN_ON(1); + return 0; + } +} + +static struct gf100_fifo_engn * +gf100_fifo_gpfifo_engine(struct gf100_fifo_chan *chan, struct nvkm_engine *engine) +{ + int engi = chan->base.fifo->func->engine_id(chan->base.fifo, engine); + if (engi >= 0) + return &chan->engn[engi]; + return NULL; +} + +static int +gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, bool suspend) +{ + const u32 offset = gf100_fifo_gpfifo_engine_addr(engine); + struct gf100_fifo_chan *chan = gf100_fifo_chan(base); + struct nvkm_subdev *subdev = &chan->fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_gpuobj *inst = chan->base.inst; + int ret = 0; + + mutex_lock(&chan->fifo->base.mutex); + nvkm_wr32(device, 0x002634, chan->base.chid); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x002634) == chan->base.chid) + break; + ) < 0) { + nvkm_error(subdev, "channel %d [%s] kick timeout\n", + chan->base.chid, chan->base.object.client->name); + ret = -ETIMEDOUT; + } + mutex_unlock(&chan->fifo->base.mutex); + + if (ret && suspend) + return ret; + + if (offset) { + nvkm_kmap(inst); + nvkm_wo32(inst, offset + 0x00, 0x00000000); + nvkm_wo32(inst, offset + 0x04, 0x00000000); + nvkm_done(inst); + } + + return ret; +} + +static int +gf100_fifo_gpfifo_engine_init(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + const u32 offset = gf100_fifo_gpfifo_engine_addr(engine); + struct gf100_fifo_chan *chan = gf100_fifo_chan(base); + struct gf100_fifo_engn *engn = gf100_fifo_gpfifo_engine(chan, engine); + struct nvkm_gpuobj *inst = chan->base.inst; + + if (offset) { + nvkm_kmap(inst); + nvkm_wo32(inst, offset + 0x00, lower_32_bits(engn->vma->addr) | 4); + nvkm_wo32(inst, offset + 0x04, upper_32_bits(engn->vma->addr)); + nvkm_done(inst); + } + + return 0; +} + +static void +gf100_fifo_gpfifo_engine_dtor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct gf100_fifo_chan *chan = gf100_fifo_chan(base); + struct gf100_fifo_engn *engn = gf100_fifo_gpfifo_engine(chan, engine); + nvkm_vmm_put(chan->base.vmm, &engn->vma); + nvkm_gpuobj_del(&engn->inst); +} + +static int +gf100_fifo_gpfifo_engine_ctor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, + struct nvkm_object *object) +{ + struct gf100_fifo_chan *chan = gf100_fifo_chan(base); + struct gf100_fifo_engn *engn = gf100_fifo_gpfifo_engine(chan, engine); + int ret; + + if (!gf100_fifo_gpfifo_engine_addr(engine)) + return 0; + + ret = nvkm_object_bind(object, NULL, 0, &engn->inst); + if (ret) + return ret; + + ret = nvkm_vmm_get(chan->base.vmm, 12, engn->inst->size, &engn->vma); + if (ret) + return ret; + + return nvkm_memory_map(engn->inst, 0, chan->base.vmm, engn->vma, NULL, 0); +} + +static void +gf100_fifo_gpfifo_fini(struct nvkm_fifo_chan *base) +{ + struct gf100_fifo_chan *chan = gf100_fifo_chan(base); + struct gf100_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 coff = chan->base.chid * 8; + + if (!list_empty(&chan->head) && !chan->killed) { + gf100_fifo_runlist_remove(fifo, chan); + nvkm_mask(device, 0x003004 + coff, 0x00000001, 0x00000000); + gf100_fifo_runlist_commit(fifo); + } + + gf100_fifo_intr_engine(fifo); + + nvkm_wr32(device, 0x003000 + coff, 0x00000000); +} + +static void +gf100_fifo_gpfifo_init(struct nvkm_fifo_chan *base) +{ + struct gf100_fifo_chan *chan = gf100_fifo_chan(base); + struct gf100_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 addr = chan->base.inst->addr >> 12; + u32 coff = chan->base.chid * 8; + + nvkm_wr32(device, 0x003000 + coff, 0xc0000000 | addr); + + if (list_empty(&chan->head) && !chan->killed) { + gf100_fifo_runlist_insert(fifo, chan); + nvkm_wr32(device, 0x003004 + coff, 0x001f0001); + gf100_fifo_runlist_commit(fifo); + } +} + +static void * +gf100_fifo_gpfifo_dtor(struct nvkm_fifo_chan *base) +{ + return gf100_fifo_chan(base); +} + +static const struct nvkm_fifo_chan_func +gf100_fifo_gpfifo_func = { + .dtor = gf100_fifo_gpfifo_dtor, + .init = gf100_fifo_gpfifo_init, + .fini = gf100_fifo_gpfifo_fini, + .ntfy = gf100_fifo_chan_ntfy, + .engine_ctor = gf100_fifo_gpfifo_engine_ctor, + .engine_dtor = gf100_fifo_gpfifo_engine_dtor, + .engine_init = gf100_fifo_gpfifo_engine_init, + .engine_fini = gf100_fifo_gpfifo_engine_fini, +}; + +static int +gf100_fifo_gpfifo_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + union { + struct fermi_channel_gpfifo_v0 v0; + } *args = data; + struct gf100_fifo *fifo = gf100_fifo(base); + struct nvkm_object *parent = oclass->parent; + struct gf100_fifo_chan *chan; + u64 usermem, ioffset, ilength; + int ret = -ENOSYS, i; + + nvif_ioctl(parent, "create channel gpfifo size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel gpfifo vers %d vmm %llx " + "ioffset %016llx ilength %08x\n", + args->v0.version, args->v0.vmm, args->v0.ioffset, + args->v0.ilength); + if (!args->v0.vmm) + return -EINVAL; + } else + return ret; + + /* allocate channel */ + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + chan->fifo = fifo; + INIT_LIST_HEAD(&chan->head); + + ret = nvkm_fifo_chan_ctor(&gf100_fifo_gpfifo_func, &fifo->base, + 0x1000, 0x1000, true, args->v0.vmm, 0, + BIT(GF100_FIFO_ENGN_GR) | + BIT(GF100_FIFO_ENGN_MSPDEC) | + BIT(GF100_FIFO_ENGN_MSPPP) | + BIT(GF100_FIFO_ENGN_MSVLD) | + BIT(GF100_FIFO_ENGN_CE0) | + BIT(GF100_FIFO_ENGN_CE1) | + BIT(GF100_FIFO_ENGN_SW), + 1, fifo->user.bar->addr, 0x1000, + oclass, &chan->base); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + + /* clear channel control registers */ + + usermem = chan->base.chid * 0x1000; + ioffset = args->v0.ioffset; + ilength = order_base_2(args->v0.ilength / 8); + + nvkm_kmap(fifo->user.mem); + for (i = 0; i < 0x1000; i += 4) + nvkm_wo32(fifo->user.mem, usermem + i, 0x00000000); + nvkm_done(fifo->user.mem); + usermem = nvkm_memory_addr(fifo->user.mem) + usermem; + + /* RAMFC */ + nvkm_kmap(chan->base.inst); + nvkm_wo32(chan->base.inst, 0x08, lower_32_bits(usermem)); + nvkm_wo32(chan->base.inst, 0x0c, upper_32_bits(usermem)); + nvkm_wo32(chan->base.inst, 0x10, 0x0000face); + nvkm_wo32(chan->base.inst, 0x30, 0xfffff902); + nvkm_wo32(chan->base.inst, 0x48, lower_32_bits(ioffset)); + nvkm_wo32(chan->base.inst, 0x4c, upper_32_bits(ioffset) | + (ilength << 16)); + nvkm_wo32(chan->base.inst, 0x54, 0x00000002); + nvkm_wo32(chan->base.inst, 0x84, 0x20400000); + nvkm_wo32(chan->base.inst, 0x94, 0x30000001); + nvkm_wo32(chan->base.inst, 0x9c, 0x00000100); + nvkm_wo32(chan->base.inst, 0xa4, 0x1f1f1f1f); + nvkm_wo32(chan->base.inst, 0xa8, 0x1f1f1f1f); + nvkm_wo32(chan->base.inst, 0xac, 0x0000001f); + nvkm_wo32(chan->base.inst, 0xb8, 0xf8000000); + nvkm_wo32(chan->base.inst, 0xf8, 0x10003080); /* 0x002310 */ + nvkm_wo32(chan->base.inst, 0xfc, 0x10000010); /* 0x002350 */ + nvkm_done(chan->base.inst); + return 0; +} + +const struct nvkm_fifo_chan_oclass +gf100_fifo_gpfifo_oclass = { + .base.oclass = FERMI_CHANNEL_GPFIFO, + .base.minver = 0, + .base.maxver = 0, + .ctor = gf100_fifo_gpfifo_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c new file mode 100644 index 000000000..80456ec70 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c @@ -0,0 +1,361 @@ +/* + * 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 "changk104.h" +#include "cgrp.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +int +gk104_fifo_gpfifo_kick_locked(struct gk104_fifo_chan *chan) +{ + struct gk104_fifo *fifo = chan->fifo; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_client *client = chan->base.object.client; + struct nvkm_fifo_cgrp *cgrp = chan->cgrp; + int ret = 0; + + if (cgrp) + nvkm_wr32(device, 0x002634, cgrp->id | 0x01000000); + else + nvkm_wr32(device, 0x002634, chan->base.chid); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x002634) & 0x00100000)) + break; + ) < 0) { + nvkm_error(subdev, "%s %d [%s] kick timeout\n", + cgrp ? "tsg" : "channel", + cgrp ? cgrp->id : chan->base.chid, client->name); + nvkm_fifo_recover_chan(&fifo->base, chan->base.chid); + ret = -ETIMEDOUT; + } + return ret; +} + +int +gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan) +{ + int ret; + mutex_lock(&chan->base.fifo->mutex); + ret = gk104_fifo_gpfifo_kick_locked(chan); + mutex_unlock(&chan->base.fifo->mutex); + return ret; +} + +static u32 +gk104_fifo_gpfifo_engine_addr(struct nvkm_engine *engine) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_SW : + case NVKM_ENGINE_CE : return 0; + case NVKM_ENGINE_GR : return 0x0210; + case NVKM_ENGINE_SEC : return 0x0220; + case NVKM_ENGINE_MSPDEC: return 0x0250; + case NVKM_ENGINE_MSPPP : return 0x0260; + case NVKM_ENGINE_MSVLD : return 0x0270; + case NVKM_ENGINE_VIC : return 0x0280; + case NVKM_ENGINE_MSENC : return 0x0290; + case NVKM_ENGINE_NVDEC : return 0x02100270; + case NVKM_ENGINE_NVENC : + if (engine->subdev.inst) + return 0x0210; + return 0x02100290; + default: + WARN_ON(1); + return 0; + } +} + +struct gk104_fifo_engn * +gk104_fifo_gpfifo_engine(struct gk104_fifo_chan *chan, struct nvkm_engine *engine) +{ + int engi = chan->base.fifo->func->engine_id(chan->base.fifo, engine); + if (engi >= 0) + return &chan->engn[engi]; + return NULL; +} + +static int +gk104_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, bool suspend) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct nvkm_gpuobj *inst = chan->base.inst; + u32 offset = gk104_fifo_gpfifo_engine_addr(engine); + int ret; + + ret = gk104_fifo_gpfifo_kick(chan); + if (ret && suspend) + return ret; + + if (offset) { + nvkm_kmap(inst); + nvkm_wo32(inst, (offset & 0xffff) + 0x00, 0x00000000); + nvkm_wo32(inst, (offset & 0xffff) + 0x04, 0x00000000); + if ((offset >>= 16)) { + nvkm_wo32(inst, offset + 0x00, 0x00000000); + nvkm_wo32(inst, offset + 0x04, 0x00000000); + } + nvkm_done(inst); + } + + return ret; +} + +static int +gk104_fifo_gpfifo_engine_init(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct gk104_fifo_engn *engn = gk104_fifo_gpfifo_engine(chan, engine); + struct nvkm_gpuobj *inst = chan->base.inst; + u32 offset = gk104_fifo_gpfifo_engine_addr(engine); + + if (offset) { + u32 datalo = lower_32_bits(engn->vma->addr) | 0x00000004; + u32 datahi = upper_32_bits(engn->vma->addr); + nvkm_kmap(inst); + nvkm_wo32(inst, (offset & 0xffff) + 0x00, datalo); + nvkm_wo32(inst, (offset & 0xffff) + 0x04, datahi); + if ((offset >>= 16)) { + nvkm_wo32(inst, offset + 0x00, datalo); + nvkm_wo32(inst, offset + 0x04, datahi); + } + nvkm_done(inst); + } + + return 0; +} + +void +gk104_fifo_gpfifo_engine_dtor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct gk104_fifo_engn *engn = gk104_fifo_gpfifo_engine(chan, engine); + nvkm_vmm_put(chan->base.vmm, &engn->vma); + nvkm_gpuobj_del(&engn->inst); +} + +int +gk104_fifo_gpfifo_engine_ctor(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, + struct nvkm_object *object) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct gk104_fifo_engn *engn = gk104_fifo_gpfifo_engine(chan, engine); + int ret; + + if (!gk104_fifo_gpfifo_engine_addr(engine)) { + if (engine->subdev.type != NVKM_ENGINE_CE || + engine->subdev.device->card_type < GV100) + return 0; + } + + ret = nvkm_object_bind(object, NULL, 0, &engn->inst); + if (ret) + return ret; + + if (!gk104_fifo_gpfifo_engine_addr(engine)) + return 0; + + ret = nvkm_vmm_get(chan->base.vmm, 12, engn->inst->size, &engn->vma); + if (ret) + return ret; + + return nvkm_memory_map(engn->inst, 0, chan->base.vmm, engn->vma, NULL, 0); +} + +void +gk104_fifo_gpfifo_fini(struct nvkm_fifo_chan *base) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct gk104_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 coff = chan->base.chid * 8; + + if (!list_empty(&chan->head)) { + gk104_fifo_runlist_remove(fifo, chan); + nvkm_mask(device, 0x800004 + coff, 0x00000800, 0x00000800); + gk104_fifo_gpfifo_kick(chan); + gk104_fifo_runlist_update(fifo, chan->runl); + } + + nvkm_wr32(device, 0x800000 + coff, 0x00000000); +} + +void +gk104_fifo_gpfifo_init(struct nvkm_fifo_chan *base) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct gk104_fifo *fifo = chan->fifo; + struct nvkm_device *device = fifo->base.engine.subdev.device; + u32 addr = chan->base.inst->addr >> 12; + u32 coff = chan->base.chid * 8; + + nvkm_mask(device, 0x800004 + coff, 0x000f0000, chan->runl << 16); + nvkm_wr32(device, 0x800000 + coff, 0x80000000 | addr); + + if (list_empty(&chan->head) && !chan->killed) { + gk104_fifo_runlist_insert(fifo, chan); + nvkm_mask(device, 0x800004 + coff, 0x00000400, 0x00000400); + gk104_fifo_runlist_update(fifo, chan->runl); + nvkm_mask(device, 0x800004 + coff, 0x00000400, 0x00000400); + } +} + +void * +gk104_fifo_gpfifo_dtor(struct nvkm_fifo_chan *base) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + kfree(chan->cgrp); + return chan; +} + +const struct nvkm_fifo_chan_func +gk104_fifo_gpfifo_func = { + .dtor = gk104_fifo_gpfifo_dtor, + .init = gk104_fifo_gpfifo_init, + .fini = gk104_fifo_gpfifo_fini, + .ntfy = gf100_fifo_chan_ntfy, + .engine_ctor = gk104_fifo_gpfifo_engine_ctor, + .engine_dtor = gk104_fifo_gpfifo_engine_dtor, + .engine_init = gk104_fifo_gpfifo_engine_init, + .engine_fini = gk104_fifo_gpfifo_engine_fini, +}; + +static int +gk104_fifo_gpfifo_new_(struct gk104_fifo *fifo, u64 *runlists, u16 *chid, + u64 vmm, u64 ioffset, u64 ilength, u64 *inst, bool priv, + const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct gk104_fifo_chan *chan; + int runlist = ffs(*runlists) -1, ret, i; + u64 usermem; + + if (!vmm || runlist < 0 || runlist >= fifo->runlist_nr) + return -EINVAL; + *runlists = BIT_ULL(runlist); + + /* Allocate the channel. */ + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + chan->fifo = fifo; + chan->runl = runlist; + INIT_LIST_HEAD(&chan->head); + + ret = nvkm_fifo_chan_ctor(&gk104_fifo_gpfifo_func, &fifo->base, + 0x1000, 0x1000, true, vmm, 0, fifo->runlist[runlist].engm_sw, + 1, fifo->user.bar->addr, 0x200, + oclass, &chan->base); + if (ret) + return ret; + + *chid = chan->base.chid; + *inst = chan->base.inst->addr; + + /* Hack to support GPUs where even individual channels should be + * part of a channel group. + */ + if (fifo->func->cgrp_force) { + if (!(chan->cgrp = kmalloc(sizeof(*chan->cgrp), GFP_KERNEL))) + return -ENOMEM; + chan->cgrp->id = chan->base.chid; + INIT_LIST_HEAD(&chan->cgrp->head); + INIT_LIST_HEAD(&chan->cgrp->chan); + chan->cgrp->chan_nr = 0; + } + + /* Clear channel control registers. */ + usermem = chan->base.chid * 0x200; + ilength = order_base_2(ilength / 8); + + nvkm_kmap(fifo->user.mem); + for (i = 0; i < 0x200; i += 4) + nvkm_wo32(fifo->user.mem, usermem + i, 0x00000000); + nvkm_done(fifo->user.mem); + usermem = nvkm_memory_addr(fifo->user.mem) + usermem; + + /* RAMFC */ + nvkm_kmap(chan->base.inst); + nvkm_wo32(chan->base.inst, 0x08, lower_32_bits(usermem)); + nvkm_wo32(chan->base.inst, 0x0c, upper_32_bits(usermem)); + nvkm_wo32(chan->base.inst, 0x10, 0x0000face); + nvkm_wo32(chan->base.inst, 0x30, 0xfffff902); + nvkm_wo32(chan->base.inst, 0x48, lower_32_bits(ioffset)); + nvkm_wo32(chan->base.inst, 0x4c, upper_32_bits(ioffset) | + (ilength << 16)); + nvkm_wo32(chan->base.inst, 0x84, 0x20400000); + nvkm_wo32(chan->base.inst, 0x94, 0x30000001); + nvkm_wo32(chan->base.inst, 0x9c, 0x00000100); + nvkm_wo32(chan->base.inst, 0xac, 0x0000001f); + nvkm_wo32(chan->base.inst, 0xe4, priv ? 0x00000020 : 0x00000000); + nvkm_wo32(chan->base.inst, 0xe8, chan->base.chid); + nvkm_wo32(chan->base.inst, 0xb8, 0xf8000000); + nvkm_wo32(chan->base.inst, 0xf8, 0x10003080); /* 0x002310 */ + nvkm_wo32(chan->base.inst, 0xfc, 0x10000010); /* 0x002350 */ + nvkm_done(chan->base.inst); + return 0; +} + +int +gk104_fifo_gpfifo_new(struct gk104_fifo *fifo, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct kepler_channel_gpfifo_a_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel gpfifo size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel gpfifo vers %d vmm %llx " + "ioffset %016llx ilength %08x " + "runlist %016llx priv %d\n", + args->v0.version, args->v0.vmm, args->v0.ioffset, + args->v0.ilength, args->v0.runlist, args->v0.priv); + return gk104_fifo_gpfifo_new_(fifo, + &args->v0.runlist, + &args->v0.chid, + args->v0.vmm, + args->v0.ioffset, + args->v0.ilength, + &args->v0.inst, + args->v0.priv, + oclass, pobject); + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogv100.c new file mode 100644 index 000000000..428f9b411 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogv100.c @@ -0,0 +1,241 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "changk104.h" +#include "cgrp.h" + +#include +#include + +#include +#include + +static u32 +gv100_fifo_gpfifo_submit_token(struct nvkm_fifo_chan *chan) +{ + return chan->chid; +} + +static int +gv100_fifo_gpfifo_engine_valid(struct gk104_fifo_chan *chan, bool ce, bool valid) +{ + struct nvkm_subdev *subdev = &chan->base.fifo->engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 mask = ce ? 0x00020000 : 0x00010000; + const u32 data = valid ? mask : 0x00000000; + int ret; + + /* Block runlist to prevent the channel from being rescheduled. */ + mutex_lock(&chan->fifo->base.mutex); + nvkm_mask(device, 0x002630, BIT(chan->runl), BIT(chan->runl)); + + /* Preempt the channel. */ + ret = gk104_fifo_gpfifo_kick_locked(chan); + if (ret == 0) { + /* Update engine context validity. */ + nvkm_kmap(chan->base.inst); + nvkm_mo32(chan->base.inst, 0x0ac, mask, data); + nvkm_done(chan->base.inst); + } + + /* Resume runlist. */ + nvkm_mask(device, 0x002630, BIT(chan->runl), 0); + mutex_unlock(&chan->fifo->base.mutex); + return ret; +} + +int +gv100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine, bool suspend) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct nvkm_gpuobj *inst = chan->base.inst; + int ret; + + if (engine->subdev.type == NVKM_ENGINE_CE) { + ret = gv100_fifo_gpfifo_engine_valid(chan, true, false); + if (ret && suspend) + return ret; + + nvkm_kmap(inst); + nvkm_wo32(chan->base.inst, 0x220, 0x00000000); + nvkm_wo32(chan->base.inst, 0x224, 0x00000000); + nvkm_done(inst); + return ret; + } + + ret = gv100_fifo_gpfifo_engine_valid(chan, false, false); + if (ret && suspend) + return ret; + + nvkm_kmap(inst); + nvkm_wo32(inst, 0x0210, 0x00000000); + nvkm_wo32(inst, 0x0214, 0x00000000); + nvkm_done(inst); + return ret; +} + +int +gv100_fifo_gpfifo_engine_init(struct nvkm_fifo_chan *base, + struct nvkm_engine *engine) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + struct gk104_fifo_engn *engn = gk104_fifo_gpfifo_engine(chan, engine); + struct nvkm_gpuobj *inst = chan->base.inst; + + if (engine->subdev.type == NVKM_ENGINE_CE) { + const u64 bar2 = nvkm_memory_bar2(engn->inst->memory); + + nvkm_kmap(inst); + nvkm_wo32(chan->base.inst, 0x220, lower_32_bits(bar2)); + nvkm_wo32(chan->base.inst, 0x224, upper_32_bits(bar2)); + nvkm_done(inst); + + return gv100_fifo_gpfifo_engine_valid(chan, true, true); + } + + nvkm_kmap(inst); + nvkm_wo32(inst, 0x210, lower_32_bits(engn->vma->addr) | 0x00000004); + nvkm_wo32(inst, 0x214, upper_32_bits(engn->vma->addr)); + nvkm_done(inst); + + return gv100_fifo_gpfifo_engine_valid(chan, false, true); +} + +static const struct nvkm_fifo_chan_func +gv100_fifo_gpfifo = { + .dtor = gk104_fifo_gpfifo_dtor, + .init = gk104_fifo_gpfifo_init, + .fini = gk104_fifo_gpfifo_fini, + .ntfy = gf100_fifo_chan_ntfy, + .engine_ctor = gk104_fifo_gpfifo_engine_ctor, + .engine_dtor = gk104_fifo_gpfifo_engine_dtor, + .engine_init = gv100_fifo_gpfifo_engine_init, + .engine_fini = gv100_fifo_gpfifo_engine_fini, + .submit_token = gv100_fifo_gpfifo_submit_token, +}; + +int +gv100_fifo_gpfifo_new_(const struct nvkm_fifo_chan_func *func, + struct gk104_fifo *fifo, u64 *runlists, u16 *chid, + u64 vmm, u64 ioffset, u64 ilength, u64 *inst, bool priv, + u32 *token, const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct gk104_fifo_chan *chan; + int runlist = ffs(*runlists) -1, ret, i; + u64 usermem; + + if (!vmm || runlist < 0 || runlist >= fifo->runlist_nr) + return -EINVAL; + *runlists = BIT_ULL(runlist); + + /* Allocate the channel. */ + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + chan->fifo = fifo; + chan->runl = runlist; + INIT_LIST_HEAD(&chan->head); + + ret = nvkm_fifo_chan_ctor(func, &fifo->base, 0x1000, 0x1000, true, vmm, + 0, fifo->runlist[runlist].engm, 1, fifo->user.bar->addr, 0x200, + oclass, &chan->base); + if (ret) + return ret; + + *chid = chan->base.chid; + *inst = chan->base.inst->addr; + *token = chan->base.func->submit_token(&chan->base); + + /* Hack to support GPUs where even individual channels should be + * part of a channel group. + */ + if (fifo->func->cgrp_force) { + if (!(chan->cgrp = kmalloc(sizeof(*chan->cgrp), GFP_KERNEL))) + return -ENOMEM; + chan->cgrp->id = chan->base.chid; + INIT_LIST_HEAD(&chan->cgrp->head); + INIT_LIST_HEAD(&chan->cgrp->chan); + chan->cgrp->chan_nr = 0; + } + + /* Clear channel control registers. */ + usermem = chan->base.chid * 0x200; + ilength = order_base_2(ilength / 8); + + nvkm_kmap(fifo->user.mem); + for (i = 0; i < 0x200; i += 4) + nvkm_wo32(fifo->user.mem, usermem + i, 0x00000000); + nvkm_done(fifo->user.mem); + usermem = nvkm_memory_addr(fifo->user.mem) + usermem; + + /* RAMFC */ + nvkm_kmap(chan->base.inst); + nvkm_wo32(chan->base.inst, 0x008, lower_32_bits(usermem)); + nvkm_wo32(chan->base.inst, 0x00c, upper_32_bits(usermem)); + nvkm_wo32(chan->base.inst, 0x010, 0x0000face); + nvkm_wo32(chan->base.inst, 0x030, 0x7ffff902); + nvkm_wo32(chan->base.inst, 0x048, lower_32_bits(ioffset)); + nvkm_wo32(chan->base.inst, 0x04c, upper_32_bits(ioffset) | + (ilength << 16)); + nvkm_wo32(chan->base.inst, 0x084, 0x20400000); + nvkm_wo32(chan->base.inst, 0x094, 0x30000001); + nvkm_wo32(chan->base.inst, 0x0e4, priv ? 0x00000020 : 0x00000000); + nvkm_wo32(chan->base.inst, 0x0e8, chan->base.chid); + nvkm_wo32(chan->base.inst, 0x0f4, 0x00001000); + nvkm_wo32(chan->base.inst, 0x0f8, 0x10003080); + nvkm_mo32(chan->base.inst, 0x218, 0x00000000, 0x00000000); + nvkm_done(chan->base.inst); + return 0; +} + +int +gv100_fifo_gpfifo_new(struct gk104_fifo *fifo, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct volta_channel_gpfifo_a_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel gpfifo size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel gpfifo vers %d vmm %llx " + "ioffset %016llx ilength %08x " + "runlist %016llx priv %d\n", + args->v0.version, args->v0.vmm, args->v0.ioffset, + args->v0.ilength, args->v0.runlist, args->v0.priv); + return gv100_fifo_gpfifo_new_(&gv100_fifo_gpfifo, fifo, + &args->v0.runlist, + &args->v0.chid, + args->v0.vmm, + args->v0.ioffset, + args->v0.ilength, + &args->v0.inst, + args->v0.priv, + &args->v0.token, + oclass, pobject); + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifonv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifonv50.c new file mode 100644 index 000000000..d8f28ec1e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifonv50.c @@ -0,0 +1,93 @@ +/* + * 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 "channv50.h" + +#include +#include + +#include +#include +#include + +static int +nv50_fifo_gpfifo_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct nv50_channel_gpfifo_v0 v0; + } *args = data; + struct nv50_fifo *fifo = nv50_fifo(base); + struct nv50_fifo_chan *chan; + u64 ioffset, ilength; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel gpfifo size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel gpfifo vers %d vmm %llx " + "pushbuf %llx ioffset %016llx " + "ilength %08x\n", + args->v0.version, args->v0.vmm, args->v0.pushbuf, + args->v0.ioffset, args->v0.ilength); + if (!args->v0.pushbuf) + return -EINVAL; + } else + return ret; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = nv50_fifo_chan_ctor(fifo, args->v0.vmm, args->v0.pushbuf, + oclass, chan); + if (ret) + return ret; + + args->v0.chid = chan->base.chid; + ioffset = args->v0.ioffset; + ilength = order_base_2(args->v0.ilength / 8); + + nvkm_kmap(chan->ramfc); + nvkm_wo32(chan->ramfc, 0x3c, 0x403f6078); + nvkm_wo32(chan->ramfc, 0x44, 0x01003fff); + nvkm_wo32(chan->ramfc, 0x48, chan->base.push->node->offset >> 4); + nvkm_wo32(chan->ramfc, 0x50, lower_32_bits(ioffset)); + nvkm_wo32(chan->ramfc, 0x54, upper_32_bits(ioffset) | (ilength << 16)); + nvkm_wo32(chan->ramfc, 0x60, 0x7fffffff); + nvkm_wo32(chan->ramfc, 0x78, 0x00000000); + nvkm_wo32(chan->ramfc, 0x7c, 0x30000001); + nvkm_wo32(chan->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj->node->offset >> 4)); + nvkm_done(chan->ramfc); + return 0; +} + +const struct nvkm_fifo_chan_oclass +nv50_fifo_gpfifo_oclass = { + .base.oclass = NV50_CHANNEL_GPFIFO, + .base.minver = 0, + .base.maxver = 0, + .ctor = nv50_fifo_gpfifo_new, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifotu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifotu102.c new file mode 100644 index 000000000..99aafa103 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifotu102.c @@ -0,0 +1,81 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "changk104.h" +#include "cgrp.h" + +#include +#include + +#include +#include + +static u32 +tu102_fifo_gpfifo_submit_token(struct nvkm_fifo_chan *base) +{ + struct gk104_fifo_chan *chan = gk104_fifo_chan(base); + return (chan->runl << 16) | chan->base.chid; +} + +static const struct nvkm_fifo_chan_func +tu102_fifo_gpfifo = { + .dtor = gk104_fifo_gpfifo_dtor, + .init = gk104_fifo_gpfifo_init, + .fini = gk104_fifo_gpfifo_fini, + .ntfy = gf100_fifo_chan_ntfy, + .engine_ctor = gk104_fifo_gpfifo_engine_ctor, + .engine_dtor = gk104_fifo_gpfifo_engine_dtor, + .engine_init = gv100_fifo_gpfifo_engine_init, + .engine_fini = gv100_fifo_gpfifo_engine_fini, + .submit_token = tu102_fifo_gpfifo_submit_token, +}; + +int +tu102_fifo_gpfifo_new(struct gk104_fifo *fifo, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_object *parent = oclass->parent; + union { + struct volta_channel_gpfifo_a_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create channel gpfifo size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create channel gpfifo vers %d vmm %llx " + "ioffset %016llx ilength %08x " + "runlist %016llx priv %d\n", + args->v0.version, args->v0.vmm, args->v0.ioffset, + args->v0.ilength, args->v0.runlist, args->v0.priv); + return gv100_fifo_gpfifo_new_(&tu102_fifo_gpfifo, fifo, + &args->v0.runlist, + &args->v0.chid, + args->v0.vmm, + args->v0.ioffset, + args->v0.ilength, + &args->v0.inst, + args->v0.priv, + &args->v0.token, + oclass, pobject); + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c new file mode 100644 index 000000000..faf0fe9f7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c @@ -0,0 +1,308 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gk104.h" +#include "cgrp.h" +#include "changk104.h" +#include "user.h" + +#include + +#include + +void +gv100_fifo_runlist_chan(struct gk104_fifo_chan *chan, + struct nvkm_memory *memory, u32 offset) +{ + struct nvkm_memory *usermem = chan->fifo->user.mem; + const u64 user = nvkm_memory_addr(usermem) + (chan->base.chid * 0x200); + const u64 inst = chan->base.inst->addr; + + nvkm_wo32(memory, offset + 0x0, lower_32_bits(user)); + nvkm_wo32(memory, offset + 0x4, upper_32_bits(user)); + nvkm_wo32(memory, offset + 0x8, lower_32_bits(inst) | chan->base.chid); + nvkm_wo32(memory, offset + 0xc, upper_32_bits(inst)); +} + +void +gv100_fifo_runlist_cgrp(struct nvkm_fifo_cgrp *cgrp, + struct nvkm_memory *memory, u32 offset) +{ + nvkm_wo32(memory, offset + 0x0, (128 << 24) | (3 << 16) | 0x00000001); + nvkm_wo32(memory, offset + 0x4, cgrp->chan_nr); + nvkm_wo32(memory, offset + 0x8, cgrp->id); + nvkm_wo32(memory, offset + 0xc, 0x00000000); +} + +static const struct gk104_fifo_runlist_func +gv100_fifo_runlist = { + .size = 16, + .cgrp = gv100_fifo_runlist_cgrp, + .chan = gv100_fifo_runlist_chan, + .commit = gk104_fifo_runlist_commit, +}; + +const struct nvkm_enum +gv100_fifo_fault_gpcclient[] = { + { 0x00, "T1_0" }, + { 0x01, "T1_1" }, + { 0x02, "T1_2" }, + { 0x03, "T1_3" }, + { 0x04, "T1_4" }, + { 0x05, "T1_5" }, + { 0x06, "T1_6" }, + { 0x07, "T1_7" }, + { 0x08, "PE_0" }, + { 0x09, "PE_1" }, + { 0x0a, "PE_2" }, + { 0x0b, "PE_3" }, + { 0x0c, "PE_4" }, + { 0x0d, "PE_5" }, + { 0x0e, "PE_6" }, + { 0x0f, "PE_7" }, + { 0x10, "RAST" }, + { 0x11, "GCC" }, + { 0x12, "GPCCS" }, + { 0x13, "PROP_0" }, + { 0x14, "PROP_1" }, + { 0x15, "PROP_2" }, + { 0x16, "PROP_3" }, + { 0x17, "GPM" }, + { 0x18, "LTP_UTLB_0" }, + { 0x19, "LTP_UTLB_1" }, + { 0x1a, "LTP_UTLB_2" }, + { 0x1b, "LTP_UTLB_3" }, + { 0x1c, "LTP_UTLB_4" }, + { 0x1d, "LTP_UTLB_5" }, + { 0x1e, "LTP_UTLB_6" }, + { 0x1f, "LTP_UTLB_7" }, + { 0x20, "RGG_UTLB" }, + { 0x21, "T1_8" }, + { 0x22, "T1_9" }, + { 0x23, "T1_10" }, + { 0x24, "T1_11" }, + { 0x25, "T1_12" }, + { 0x26, "T1_13" }, + { 0x27, "T1_14" }, + { 0x28, "T1_15" }, + { 0x29, "TPCCS_0" }, + { 0x2a, "TPCCS_1" }, + { 0x2b, "TPCCS_2" }, + { 0x2c, "TPCCS_3" }, + { 0x2d, "TPCCS_4" }, + { 0x2e, "TPCCS_5" }, + { 0x2f, "TPCCS_6" }, + { 0x30, "TPCCS_7" }, + { 0x31, "PE_8" }, + { 0x32, "PE_9" }, + { 0x33, "TPCCS_8" }, + { 0x34, "TPCCS_9" }, + { 0x35, "T1_16" }, + { 0x36, "T1_17" }, + { 0x37, "T1_18" }, + { 0x38, "T1_19" }, + { 0x39, "PE_10" }, + { 0x3a, "PE_11" }, + { 0x3b, "TPCCS_10" }, + { 0x3c, "TPCCS_11" }, + { 0x3d, "T1_20" }, + { 0x3e, "T1_21" }, + { 0x3f, "T1_22" }, + { 0x40, "T1_23" }, + { 0x41, "PE_12" }, + { 0x42, "PE_13" }, + { 0x43, "TPCCS_12" }, + { 0x44, "TPCCS_13" }, + { 0x45, "T1_24" }, + { 0x46, "T1_25" }, + { 0x47, "T1_26" }, + { 0x48, "T1_27" }, + { 0x49, "PE_14" }, + { 0x4a, "PE_15" }, + { 0x4b, "TPCCS_14" }, + { 0x4c, "TPCCS_15" }, + { 0x4d, "T1_28" }, + { 0x4e, "T1_29" }, + { 0x4f, "T1_30" }, + { 0x50, "T1_31" }, + { 0x51, "PE_16" }, + { 0x52, "PE_17" }, + { 0x53, "TPCCS_16" }, + { 0x54, "TPCCS_17" }, + { 0x55, "T1_32" }, + { 0x56, "T1_33" }, + { 0x57, "T1_34" }, + { 0x58, "T1_35" }, + { 0x59, "PE_18" }, + { 0x5a, "PE_19" }, + { 0x5b, "TPCCS_18" }, + { 0x5c, "TPCCS_19" }, + { 0x5d, "T1_36" }, + { 0x5e, "T1_37" }, + { 0x5f, "T1_38" }, + { 0x60, "T1_39" }, + {} +}; + +const struct nvkm_enum +gv100_fifo_fault_hubclient[] = { + { 0x00, "VIP" }, + { 0x01, "CE0" }, + { 0x02, "CE1" }, + { 0x03, "DNISO" }, + { 0x04, "FE" }, + { 0x05, "FECS" }, + { 0x06, "HOST" }, + { 0x07, "HOST_CPU" }, + { 0x08, "HOST_CPU_NB" }, + { 0x09, "ISO" }, + { 0x0a, "MMU" }, + { 0x0b, "NVDEC" }, + { 0x0d, "NVENC1" }, + { 0x0e, "NISO" }, + { 0x0f, "P2P" }, + { 0x10, "PD" }, + { 0x11, "PERF" }, + { 0x12, "PMU" }, + { 0x13, "RASTERTWOD" }, + { 0x14, "SCC" }, + { 0x15, "SCC_NB" }, + { 0x16, "SEC" }, + { 0x17, "SSYNC" }, + { 0x18, "CE2" }, + { 0x19, "XV" }, + { 0x1a, "MMU_NB" }, + { 0x1b, "NVENC0" }, + { 0x1c, "DFALCON" }, + { 0x1d, "SKED" }, + { 0x1e, "AFALCON" }, + { 0x1f, "DONT_CARE" }, + { 0x20, "HSCE0" }, + { 0x21, "HSCE1" }, + { 0x22, "HSCE2" }, + { 0x23, "HSCE3" }, + { 0x24, "HSCE4" }, + { 0x25, "HSCE5" }, + { 0x26, "HSCE6" }, + { 0x27, "HSCE7" }, + { 0x28, "HSCE8" }, + { 0x29, "HSCE9" }, + { 0x2a, "HSHUB" }, + { 0x2b, "PTP_X0" }, + { 0x2c, "PTP_X1" }, + { 0x2d, "PTP_X2" }, + { 0x2e, "PTP_X3" }, + { 0x2f, "PTP_X4" }, + { 0x30, "PTP_X5" }, + { 0x31, "PTP_X6" }, + { 0x32, "PTP_X7" }, + { 0x33, "NVENC2" }, + { 0x34, "VPR_SCRUBBER0" }, + { 0x35, "VPR_SCRUBBER1" }, + { 0x36, "DWBIF" }, + { 0x37, "FBFALCON" }, + { 0x38, "CE_SHIM" }, + { 0x39, "GSP" }, + {} +}; + +const struct nvkm_enum +gv100_fifo_fault_reason[] = { + { 0x00, "PDE" }, + { 0x01, "PDE_SIZE" }, + { 0x02, "PTE" }, + { 0x03, "VA_LIMIT_VIOLATION" }, + { 0x04, "UNBOUND_INST_BLOCK" }, + { 0x05, "PRIV_VIOLATION" }, + { 0x06, "RO_VIOLATION" }, + { 0x07, "WO_VIOLATION" }, + { 0x08, "PITCH_MASK_VIOLATION" }, + { 0x09, "WORK_CREATION" }, + { 0x0a, "UNSUPPORTED_APERTURE" }, + { 0x0b, "COMPRESSION_FAILURE" }, + { 0x0c, "UNSUPPORTED_KIND" }, + { 0x0d, "REGION_VIOLATION" }, + { 0x0e, "POISONED" }, + { 0x0f, "ATOMIC_VIOLATION" }, + {} +}; + +static const struct nvkm_enum +gv100_fifo_fault_engine[] = { + { 0x01, "DISPLAY" }, + { 0x03, "PTP" }, + { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, + { 0x05, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, + { 0x06, "PWR_PMU" }, + { 0x08, "IFB", NULL, NVKM_ENGINE_IFB }, + { 0x09, "PERF" }, + { 0x1f, "PHYSICAL" }, + { 0x20, "HOST0" }, + { 0x21, "HOST1" }, + { 0x22, "HOST2" }, + { 0x23, "HOST3" }, + { 0x24, "HOST4" }, + { 0x25, "HOST5" }, + { 0x26, "HOST6" }, + { 0x27, "HOST7" }, + { 0x28, "HOST8" }, + { 0x29, "HOST9" }, + { 0x2a, "HOST10" }, + { 0x2b, "HOST11" }, + { 0x2c, "HOST12" }, + { 0x2d, "HOST13" }, + {} +}; + +const struct nvkm_enum +gv100_fifo_fault_access[] = { + { 0x0, "VIRT_READ" }, + { 0x1, "VIRT_WRITE" }, + { 0x2, "VIRT_ATOMIC" }, + { 0x3, "VIRT_PREFETCH" }, + { 0x4, "VIRT_ATOMIC_WEAK" }, + { 0x8, "PHYS_READ" }, + { 0x9, "PHYS_WRITE" }, + { 0xa, "PHYS_ATOMIC" }, + { 0xb, "PHYS_PREFETCH" }, + {} +}; + +static const struct gk104_fifo_func +gv100_fifo = { + .pbdma = &gm200_fifo_pbdma, + .fault.access = gv100_fifo_fault_access, + .fault.engine = gv100_fifo_fault_engine, + .fault.reason = gv100_fifo_fault_reason, + .fault.hubclient = gv100_fifo_fault_hubclient, + .fault.gpcclient = gv100_fifo_fault_gpcclient, + .runlist = &gv100_fifo_runlist, + .user = {{-1,-1,VOLTA_USERMODE_A }, gv100_fifo_user_new }, + .chan = {{ 0, 0,VOLTA_CHANNEL_GPFIFO_A}, gv100_fifo_gpfifo_new }, + .cgrp_force = true, +}; + +int +gv100_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return gk104_fifo_new_(&gv100_fifo, device, type, inst, 4096, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c new file mode 100644 index 000000000..c6730c124 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c @@ -0,0 +1,399 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" +#include "channv04.h" +#include "regsnv04.h" + +#include +#include +#include +#include +#include + +static const struct nv04_fifo_ramfc +nv04_fifo_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 16, 0, 0x08, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 16, 16, 0x08, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x18, 0, NV04_PFIFO_CACHE1_PULL1 }, + {} +}; + +void +nv04_fifo_pause(struct nvkm_fifo *base, unsigned long *pflags) +__acquires(fifo->base.lock) +{ + struct nv04_fifo *fifo = nv04_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + unsigned long flags; + + spin_lock_irqsave(&fifo->base.lock, flags); + *pflags = flags; + + nvkm_wr32(device, NV03_PFIFO_CACHES, 0x00000000); + nvkm_mask(device, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0x00000000); + + /* in some cases the puller may be left in an inconsistent state + * if you try to stop it while it's busy translating handles. + * sometimes you get a CACHE_ERROR, sometimes it just fails + * silently; sending incorrect instance offsets to PGRAPH after + * it's started up again. + * + * to avoid this, we invalidate the most recently calculated + * instance. + */ + nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, NV04_PFIFO_CACHE1_PULL0); + if (!(tmp & NV04_PFIFO_CACHE1_PULL0_HASH_BUSY)) + break; + ); + + if (nvkm_rd32(device, NV04_PFIFO_CACHE1_PULL0) & + NV04_PFIFO_CACHE1_PULL0_HASH_FAILED) + nvkm_wr32(device, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR); + + nvkm_wr32(device, NV04_PFIFO_CACHE1_HASH, 0x00000000); +} + +void +nv04_fifo_start(struct nvkm_fifo *base, unsigned long *pflags) +__releases(fifo->base.lock) +{ + struct nv04_fifo *fifo = nv04_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + unsigned long flags = *pflags; + + nvkm_mask(device, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0x00000001); + nvkm_wr32(device, NV03_PFIFO_CACHES, 0x00000001); + + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +struct nvkm_engine * +nv04_fifo_id_engine(struct nvkm_fifo *fifo, int engi) +{ + enum nvkm_subdev_type type; + + switch (engi) { + case NV04_FIFO_ENGN_SW : type = NVKM_ENGINE_SW; break; + case NV04_FIFO_ENGN_GR : type = NVKM_ENGINE_GR; break; + case NV04_FIFO_ENGN_MPEG: type = NVKM_ENGINE_MPEG; break; + case NV04_FIFO_ENGN_DMA : type = NVKM_ENGINE_DMAOBJ; break; + default: + WARN_ON(1); + return NULL; + } + + return nvkm_device_engine(fifo->engine.subdev.device, type, 0); +} + +int +nv04_fifo_engine_id(struct nvkm_fifo *base, struct nvkm_engine *engine) +{ + switch (engine->subdev.type) { + case NVKM_ENGINE_SW : return NV04_FIFO_ENGN_SW; + case NVKM_ENGINE_GR : return NV04_FIFO_ENGN_GR; + case NVKM_ENGINE_MPEG : return NV04_FIFO_ENGN_MPEG; + case NVKM_ENGINE_DMAOBJ: return NV04_FIFO_ENGN_DMA; + default: + WARN_ON(1); + return 0; + } +} + +static const char * +nv_dma_state_err(u32 state) +{ + static const char * const desc[] = { + "NONE", "CALL_SUBR_ACTIVE", "INVALID_MTHD", "RET_SUBR_INACTIVE", + "INVALID_CMD", "IB_EMPTY"/* NV50+ */, "MEM_FAULT", "UNK" + }; + return desc[(state >> 29) & 0x7]; +} + +static bool +nv04_fifo_swmthd(struct nvkm_device *device, u32 chid, u32 addr, u32 data) +{ + struct nvkm_sw *sw = device->sw; + const int subc = (addr & 0x0000e000) >> 13; + const int mthd = (addr & 0x00001ffc); + const u32 mask = 0x0000000f << (subc * 4); + u32 engine = nvkm_rd32(device, 0x003280); + bool handled = false; + + switch (mthd) { + case 0x0000 ... 0x0000: /* subchannel's engine -> software */ + nvkm_wr32(device, 0x003280, (engine &= ~mask)); + fallthrough; + case 0x0180 ... 0x01fc: /* handle -> instance */ + data = nvkm_rd32(device, 0x003258) & 0x0000ffff; + fallthrough; + case 0x0100 ... 0x017c: + case 0x0200 ... 0x1ffc: /* pass method down to sw */ + if (!(engine & mask) && sw) + handled = nvkm_sw_mthd(sw, chid, subc, mthd, data); + break; + default: + break; + } + + return handled; +} + +static void +nv04_fifo_cache_error(struct nv04_fifo *fifo, u32 chid, u32 get) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fifo_chan *chan; + unsigned long flags; + u32 pull0 = nvkm_rd32(device, 0x003250); + u32 mthd, data; + int ptr; + + /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before wrapping on my + * G80 chips, but CACHE1 isn't big enough for this much data.. Tests + * show that it wraps around to the start at GET=0x800.. No clue as to + * why.. + */ + ptr = (get & 0x7ff) >> 2; + + if (device->card_type < NV_40) { + mthd = nvkm_rd32(device, NV04_PFIFO_CACHE1_METHOD(ptr)); + data = nvkm_rd32(device, NV04_PFIFO_CACHE1_DATA(ptr)); + } else { + mthd = nvkm_rd32(device, NV40_PFIFO_CACHE1_METHOD(ptr)); + data = nvkm_rd32(device, NV40_PFIFO_CACHE1_DATA(ptr)); + } + + if (!(pull0 & 0x00000100) || + !nv04_fifo_swmthd(device, chid, mthd, data)) { + chan = nvkm_fifo_chan_chid(&fifo->base, chid, &flags); + nvkm_error(subdev, "CACHE_ERROR - " + "ch %d [%s] subc %d mthd %04x data %08x\n", + chid, chan ? chan->object.client->name : "unknown", + (mthd >> 13) & 7, mthd & 0x1ffc, data); + nvkm_fifo_chan_put(&fifo->base, flags, &chan); + } + + nvkm_wr32(device, NV04_PFIFO_CACHE1_DMA_PUSH, 0); + nvkm_wr32(device, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH0, + nvkm_rd32(device, NV03_PFIFO_CACHE1_PUSH0) & ~1); + nvkm_wr32(device, NV03_PFIFO_CACHE1_GET, get + 4); + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH0, + nvkm_rd32(device, NV03_PFIFO_CACHE1_PUSH0) | 1); + nvkm_wr32(device, NV04_PFIFO_CACHE1_HASH, 0); + + nvkm_wr32(device, NV04_PFIFO_CACHE1_DMA_PUSH, + nvkm_rd32(device, NV04_PFIFO_CACHE1_DMA_PUSH) | 1); + nvkm_wr32(device, NV04_PFIFO_CACHE1_PULL0, 1); +} + +static void +nv04_fifo_dma_pusher(struct nv04_fifo *fifo, u32 chid) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 dma_get = nvkm_rd32(device, 0x003244); + u32 dma_put = nvkm_rd32(device, 0x003240); + u32 push = nvkm_rd32(device, 0x003220); + u32 state = nvkm_rd32(device, 0x003228); + struct nvkm_fifo_chan *chan; + unsigned long flags; + const char *name; + + chan = nvkm_fifo_chan_chid(&fifo->base, chid, &flags); + name = chan ? chan->object.client->name : "unknown"; + if (device->card_type == NV_50) { + u32 ho_get = nvkm_rd32(device, 0x003328); + u32 ho_put = nvkm_rd32(device, 0x003320); + u32 ib_get = nvkm_rd32(device, 0x003334); + u32 ib_put = nvkm_rd32(device, 0x003330); + + nvkm_error(subdev, "DMA_PUSHER - " + "ch %d [%s] get %02x%08x put %02x%08x ib_get %08x " + "ib_put %08x state %08x (err: %s) push %08x\n", + chid, name, ho_get, dma_get, ho_put, dma_put, + ib_get, ib_put, state, nv_dma_state_err(state), + push); + + /* METHOD_COUNT, in DMA_STATE on earlier chipsets */ + nvkm_wr32(device, 0x003364, 0x00000000); + if (dma_get != dma_put || ho_get != ho_put) { + nvkm_wr32(device, 0x003244, dma_put); + nvkm_wr32(device, 0x003328, ho_put); + } else + if (ib_get != ib_put) + nvkm_wr32(device, 0x003334, ib_put); + } else { + nvkm_error(subdev, "DMA_PUSHER - ch %d [%s] get %08x put %08x " + "state %08x (err: %s) push %08x\n", + chid, name, dma_get, dma_put, state, + nv_dma_state_err(state), push); + + if (dma_get != dma_put) + nvkm_wr32(device, 0x003244, dma_put); + } + nvkm_fifo_chan_put(&fifo->base, flags, &chan); + + nvkm_wr32(device, 0x003228, 0x00000000); + nvkm_wr32(device, 0x003220, 0x00000001); + nvkm_wr32(device, 0x002100, NV_PFIFO_INTR_DMA_PUSHER); +} + +void +nv04_fifo_intr(struct nvkm_fifo *base) +{ + struct nv04_fifo *fifo = nv04_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mask = nvkm_rd32(device, NV03_PFIFO_INTR_EN_0); + u32 stat = nvkm_rd32(device, NV03_PFIFO_INTR_0) & mask; + u32 reassign, chid, get, sem; + + reassign = nvkm_rd32(device, NV03_PFIFO_CACHES) & 1; + nvkm_wr32(device, NV03_PFIFO_CACHES, 0); + + chid = nvkm_rd32(device, NV03_PFIFO_CACHE1_PUSH1) & (fifo->base.nr - 1); + get = nvkm_rd32(device, NV03_PFIFO_CACHE1_GET); + + if (stat & NV_PFIFO_INTR_CACHE_ERROR) { + nv04_fifo_cache_error(fifo, chid, get); + stat &= ~NV_PFIFO_INTR_CACHE_ERROR; + } + + if (stat & NV_PFIFO_INTR_DMA_PUSHER) { + nv04_fifo_dma_pusher(fifo, chid); + stat &= ~NV_PFIFO_INTR_DMA_PUSHER; + } + + if (stat & NV_PFIFO_INTR_SEMAPHORE) { + stat &= ~NV_PFIFO_INTR_SEMAPHORE; + nvkm_wr32(device, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_SEMAPHORE); + + sem = nvkm_rd32(device, NV10_PFIFO_CACHE1_SEMAPHORE); + nvkm_wr32(device, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_GET, get + 4); + nvkm_wr32(device, NV04_PFIFO_CACHE1_PULL0, 1); + } + + if (device->card_type == NV_50) { + if (stat & 0x00000010) { + stat &= ~0x00000010; + nvkm_wr32(device, 0x002100, 0x00000010); + } + + if (stat & 0x40000000) { + nvkm_wr32(device, 0x002100, 0x40000000); + nvkm_fifo_uevent(&fifo->base); + stat &= ~0x40000000; + } + } + + if (stat) { + nvkm_warn(subdev, "intr %08x\n", stat); + nvkm_mask(device, NV03_PFIFO_INTR_EN_0, stat, 0x00000000); + nvkm_wr32(device, NV03_PFIFO_INTR_0, stat); + } + + nvkm_wr32(device, NV03_PFIFO_CACHES, reassign); +} + +void +nv04_fifo_init(struct nvkm_fifo *base) +{ + struct nv04_fifo *fifo = nv04_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + struct nvkm_ramht *ramht = imem->ramht; + struct nvkm_memory *ramro = imem->ramro; + struct nvkm_memory *ramfc = imem->ramfc; + + nvkm_wr32(device, NV04_PFIFO_DELAY_0, 0x000000ff); + nvkm_wr32(device, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff); + + nvkm_wr32(device, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | + ((ramht->bits - 9) << 16) | + (ramht->gpuobj->addr >> 8)); + nvkm_wr32(device, NV03_PFIFO_RAMRO, nvkm_memory_addr(ramro) >> 8); + nvkm_wr32(device, NV03_PFIFO_RAMFC, nvkm_memory_addr(ramfc) >> 8); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH1, fifo->base.nr - 1); + + nvkm_wr32(device, NV03_PFIFO_INTR_0, 0xffffffff); + nvkm_wr32(device, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH0, 1); + nvkm_wr32(device, NV04_PFIFO_CACHE1_PULL0, 1); + nvkm_wr32(device, NV03_PFIFO_CACHES, 1); +} + +int +nv04_fifo_new_(const struct nvkm_fifo_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, int nr, const struct nv04_fifo_ramfc *ramfc, + struct nvkm_fifo **pfifo) +{ + struct nv04_fifo *fifo; + int ret; + + if (!(fifo = kzalloc(sizeof(*fifo), GFP_KERNEL))) + return -ENOMEM; + fifo->ramfc = ramfc; + *pfifo = &fifo->base; + + ret = nvkm_fifo_ctor(func, device, type, inst, nr, &fifo->base); + if (ret) + return ret; + + set_bit(nr - 1, fifo->base.mask); /* inactive channel */ + return 0; +} + +static const struct nvkm_fifo_func +nv04_fifo = { + .init = nv04_fifo_init, + .intr = nv04_fifo_intr, + .engine_id = nv04_fifo_engine_id, + .id_engine = nv04_fifo_id_engine, + .pause = nv04_fifo_pause, + .start = nv04_fifo_start, + .chan = { + &nv04_fifo_dma_oclass, + NULL + }, +}; + +int +nv04_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return nv04_fifo_new_(&nv04_fifo, device, type, inst, 16, nv04_fifo_ramfc, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h new file mode 100644 index 000000000..3f23bcde4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV04_FIFO_H__ +#define __NV04_FIFO_H__ +#define nv04_fifo(p) container_of((p), struct nv04_fifo, base) +#include "priv.h" + +struct nv04_fifo_ramfc { + unsigned bits:6; + unsigned ctxs:5; + unsigned ctxp:8; + unsigned regs:5; + unsigned regp; +}; + +struct nv04_fifo { + struct nvkm_fifo base; + const struct nv04_fifo_ramfc *ramfc; +}; + +int nv04_fifo_new_(const struct nvkm_fifo_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + int nr, const struct nv04_fifo_ramfc *, struct nvkm_fifo **); +void nv04_fifo_init(struct nvkm_fifo *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c new file mode 100644 index 000000000..f8887f0f2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c @@ -0,0 +1,61 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" +#include "channv04.h" +#include "regsnv04.h" + +static const struct nv04_fifo_ramfc +nv10_fifo_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT }, + { 16, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 16, 16, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 32, 0, 0x18, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x1c, 0, NV04_PFIFO_CACHE1_PULL1 }, + {} +}; + +static const struct nvkm_fifo_func +nv10_fifo = { + .init = nv04_fifo_init, + .intr = nv04_fifo_intr, + .engine_id = nv04_fifo_engine_id, + .id_engine = nv04_fifo_id_engine, + .pause = nv04_fifo_pause, + .start = nv04_fifo_start, + .chan = { + &nv10_fifo_dma_oclass, + NULL + }, +}; + +int +nv10_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return nv04_fifo_new_(&nv10_fifo, device, type, inst, 32, nv10_fifo_ramfc, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c new file mode 100644 index 000000000..3f94c7b5b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c @@ -0,0 +1,99 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" +#include "channv04.h" +#include "regsnv04.h" + +#include +#include + +static const struct nv04_fifo_ramfc +nv17_fifo_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT }, + { 16, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 16, 16, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 32, 0, 0x18, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x1c, 0, NV04_PFIFO_CACHE1_PULL1 }, + { 32, 0, 0x20, 0, NV10_PFIFO_CACHE1_ACQUIRE_VALUE }, + { 32, 0, 0x24, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP }, + { 32, 0, 0x28, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT }, + { 32, 0, 0x2c, 0, NV10_PFIFO_CACHE1_SEMAPHORE }, + { 32, 0, 0x30, 0, NV10_PFIFO_CACHE1_DMA_SUBROUTINE }, + {} +}; + +static void +nv17_fifo_init(struct nvkm_fifo *base) +{ + struct nv04_fifo *fifo = nv04_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_instmem *imem = device->imem; + struct nvkm_ramht *ramht = imem->ramht; + struct nvkm_memory *ramro = imem->ramro; + struct nvkm_memory *ramfc = imem->ramfc; + + nvkm_wr32(device, NV04_PFIFO_DELAY_0, 0x000000ff); + nvkm_wr32(device, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff); + + nvkm_wr32(device, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | + ((ramht->bits - 9) << 16) | + (ramht->gpuobj->addr >> 8)); + nvkm_wr32(device, NV03_PFIFO_RAMRO, nvkm_memory_addr(ramro) >> 8); + nvkm_wr32(device, NV03_PFIFO_RAMFC, nvkm_memory_addr(ramfc) >> 8 | + 0x00010000); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH1, fifo->base.nr - 1); + + nvkm_wr32(device, NV03_PFIFO_INTR_0, 0xffffffff); + nvkm_wr32(device, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH0, 1); + nvkm_wr32(device, NV04_PFIFO_CACHE1_PULL0, 1); + nvkm_wr32(device, NV03_PFIFO_CACHES, 1); +} + +static const struct nvkm_fifo_func +nv17_fifo = { + .init = nv17_fifo_init, + .intr = nv04_fifo_intr, + .engine_id = nv04_fifo_engine_id, + .id_engine = nv04_fifo_id_engine, + .pause = nv04_fifo_pause, + .start = nv04_fifo_start, + .chan = { + &nv17_fifo_dma_oclass, + NULL + }, +}; + +int +nv17_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return nv04_fifo_new_(&nv17_fifo, device, type, inst, 32, nv17_fifo_ramfc, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c new file mode 100644 index 000000000..f9ea46809 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c @@ -0,0 +1,130 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" +#include "channv04.h" +#include "regsnv04.h" + +#include +#include +#include + +static const struct nv04_fifo_ramfc +nv40_fifo_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT }, + { 32, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 28, 0, 0x18, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 2, 28, 0x18, 28, 0x002058 }, + { 32, 0, 0x1c, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x20, 0, NV04_PFIFO_CACHE1_PULL1 }, + { 32, 0, 0x24, 0, NV10_PFIFO_CACHE1_ACQUIRE_VALUE }, + { 32, 0, 0x28, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP }, + { 32, 0, 0x2c, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT }, + { 32, 0, 0x30, 0, NV10_PFIFO_CACHE1_SEMAPHORE }, + { 32, 0, 0x34, 0, NV10_PFIFO_CACHE1_DMA_SUBROUTINE }, + { 32, 0, 0x38, 0, NV40_PFIFO_GRCTX_INSTANCE }, + { 17, 0, 0x3c, 0, NV04_PFIFO_DMA_TIMESLICE }, + { 32, 0, 0x40, 0, 0x0032e4 }, + { 32, 0, 0x44, 0, 0x0032e8 }, + { 32, 0, 0x4c, 0, 0x002088 }, + { 32, 0, 0x50, 0, 0x003300 }, + { 32, 0, 0x54, 0, 0x00330c }, + {} +}; + +static void +nv40_fifo_init(struct nvkm_fifo *base) +{ + struct nv04_fifo *fifo = nv04_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_fb *fb = device->fb; + struct nvkm_instmem *imem = device->imem; + struct nvkm_ramht *ramht = imem->ramht; + struct nvkm_memory *ramro = imem->ramro; + struct nvkm_memory *ramfc = imem->ramfc; + + nvkm_wr32(device, 0x002040, 0x000000ff); + nvkm_wr32(device, 0x002044, 0x2101ffff); + nvkm_wr32(device, 0x002058, 0x00000001); + + nvkm_wr32(device, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | + ((ramht->bits - 9) << 16) | + (ramht->gpuobj->addr >> 8)); + nvkm_wr32(device, NV03_PFIFO_RAMRO, nvkm_memory_addr(ramro) >> 8); + + switch (device->chipset) { + case 0x47: + case 0x49: + case 0x4b: + nvkm_wr32(device, 0x002230, 0x00000001); + fallthrough; + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x45: + case 0x48: + nvkm_wr32(device, 0x002220, 0x00030002); + break; + default: + nvkm_wr32(device, 0x002230, 0x00000000); + nvkm_wr32(device, 0x002220, ((fb->ram->size - 512 * 1024 + + nvkm_memory_addr(ramfc)) >> 16) | + 0x00030000); + break; + } + + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH1, fifo->base.nr - 1); + + nvkm_wr32(device, NV03_PFIFO_INTR_0, 0xffffffff); + nvkm_wr32(device, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nvkm_wr32(device, NV03_PFIFO_CACHE1_PUSH0, 1); + nvkm_wr32(device, NV04_PFIFO_CACHE1_PULL0, 1); + nvkm_wr32(device, NV03_PFIFO_CACHES, 1); +} + +static const struct nvkm_fifo_func +nv40_fifo = { + .init = nv40_fifo_init, + .intr = nv04_fifo_intr, + .engine_id = nv04_fifo_engine_id, + .id_engine = nv04_fifo_id_engine, + .pause = nv04_fifo_pause, + .start = nv04_fifo_start, + .chan = { + &nv40_fifo_dma_oclass, + NULL + }, +}; + +int +nv40_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return nv04_fifo_new_(&nv40_fifo, device, type, inst, 32, nv40_fifo_ramfc, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c new file mode 100644 index 000000000..a08742cf4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c @@ -0,0 +1,149 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "channv50.h" + +#include + +static void +nv50_fifo_runlist_update_locked(struct nv50_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_memory *cur; + int i, p; + + cur = fifo->runlist[fifo->cur_runlist]; + fifo->cur_runlist = !fifo->cur_runlist; + + nvkm_kmap(cur); + for (i = 0, p = 0; i < fifo->base.nr; i++) { + if (nvkm_rd32(device, 0x002600 + (i * 4)) & 0x80000000) + nvkm_wo32(cur, p++ * 4, i); + } + nvkm_done(cur); + + nvkm_wr32(device, 0x0032f4, nvkm_memory_addr(cur) >> 12); + nvkm_wr32(device, 0x0032ec, p); + nvkm_wr32(device, 0x002500, 0x00000101); +} + +void +nv50_fifo_runlist_update(struct nv50_fifo *fifo) +{ + mutex_lock(&fifo->base.mutex); + nv50_fifo_runlist_update_locked(fifo); + mutex_unlock(&fifo->base.mutex); +} + +int +nv50_fifo_oneinit(struct nvkm_fifo *base) +{ + struct nv50_fifo *fifo = nv50_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + int ret; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 128 * 4, 0x1000, + false, &fifo->runlist[0]); + if (ret) + return ret; + + return nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 128 * 4, 0x1000, + false, &fifo->runlist[1]); +} + +void +nv50_fifo_init(struct nvkm_fifo *base) +{ + struct nv50_fifo *fifo = nv50_fifo(base); + struct nvkm_device *device = fifo->base.engine.subdev.device; + int i; + + nvkm_mask(device, 0x000200, 0x00000100, 0x00000000); + nvkm_mask(device, 0x000200, 0x00000100, 0x00000100); + nvkm_wr32(device, 0x00250c, 0x6f3cfc34); + nvkm_wr32(device, 0x002044, 0x01003fff); + + nvkm_wr32(device, 0x002100, 0xffffffff); + nvkm_wr32(device, 0x002140, 0xbfffffff); + + for (i = 0; i < 128; i++) + nvkm_wr32(device, 0x002600 + (i * 4), 0x00000000); + nv50_fifo_runlist_update_locked(fifo); + + nvkm_wr32(device, 0x003200, 0x00000001); + nvkm_wr32(device, 0x003250, 0x00000001); + nvkm_wr32(device, 0x002500, 0x00000001); +} + +void * +nv50_fifo_dtor(struct nvkm_fifo *base) +{ + struct nv50_fifo *fifo = nv50_fifo(base); + nvkm_memory_unref(&fifo->runlist[1]); + nvkm_memory_unref(&fifo->runlist[0]); + return fifo; +} + +int +nv50_fifo_new_(const struct nvkm_fifo_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fifo **pfifo) +{ + struct nv50_fifo *fifo; + int ret; + + if (!(fifo = kzalloc(sizeof(*fifo), GFP_KERNEL))) + return -ENOMEM; + *pfifo = &fifo->base; + + ret = nvkm_fifo_ctor(func, device, type, inst, 128, &fifo->base); + if (ret) + return ret; + + set_bit(0, fifo->base.mask); /* PIO channel */ + set_bit(127, fifo->base.mask); /* inactive channel */ + return 0; +} + +static const struct nvkm_fifo_func +nv50_fifo = { + .dtor = nv50_fifo_dtor, + .oneinit = nv50_fifo_oneinit, + .init = nv50_fifo_init, + .intr = nv04_fifo_intr, + .engine_id = nv04_fifo_engine_id, + .id_engine = nv04_fifo_id_engine, + .pause = nv04_fifo_pause, + .start = nv04_fifo_start, + .chan = { + &nv50_fifo_gpfifo_oclass, + NULL + }, +}; + +int +nv50_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + return nv50_fifo_new_(&nv50_fifo, device, type, inst, pfifo); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h new file mode 100644 index 000000000..0111e7e5a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_FIFO_H__ +#define __NV50_FIFO_H__ +#define nv50_fifo(p) container_of((p), struct nv50_fifo, base) +#include "priv.h" + +struct nv50_fifo { + struct nvkm_fifo base; + struct nvkm_memory *runlist[2]; + int cur_runlist; +}; + +int nv50_fifo_new_(const struct nvkm_fifo_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_fifo **); + +void *nv50_fifo_dtor(struct nvkm_fifo *); +int nv50_fifo_oneinit(struct nvkm_fifo *); +void nv50_fifo_init(struct nvkm_fifo *); +void nv50_fifo_runlist_update(struct nv50_fifo *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h new file mode 100644 index 000000000..79cec5764 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FIFO_PRIV_H__ +#define __NVKM_FIFO_PRIV_H__ +#define nvkm_fifo(p) container_of((p), struct nvkm_fifo, engine) +#include + +int nvkm_fifo_ctor(const struct nvkm_fifo_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + int nr, struct nvkm_fifo *); +void nvkm_fifo_uevent(struct nvkm_fifo *); +void nvkm_fifo_kevent(struct nvkm_fifo *, int chid); +void nvkm_fifo_recover_chan(struct nvkm_fifo *, int chid); + +struct nvkm_fifo_chan * +nvkm_fifo_chan_inst_locked(struct nvkm_fifo *, u64 inst); + +struct nvkm_fifo_chan_oclass; +struct nvkm_fifo_func { + void *(*dtor)(struct nvkm_fifo *); + int (*oneinit)(struct nvkm_fifo *); + int (*info)(struct nvkm_fifo *, u64 mthd, u64 *data); + void (*init)(struct nvkm_fifo *); + void (*fini)(struct nvkm_fifo *); + void (*intr)(struct nvkm_fifo *); + void (*fault)(struct nvkm_fifo *, struct nvkm_fault_data *); + int (*engine_id)(struct nvkm_fifo *, struct nvkm_engine *); + struct nvkm_engine *(*id_engine)(struct nvkm_fifo *, int engi); + void (*pause)(struct nvkm_fifo *, unsigned long *); + void (*start)(struct nvkm_fifo *, unsigned long *); + void (*uevent_init)(struct nvkm_fifo *); + void (*uevent_fini)(struct nvkm_fifo *); + void (*recover_chan)(struct nvkm_fifo *, int chid); + int (*class_get)(struct nvkm_fifo *, int index, struct nvkm_oclass *); + int (*class_new)(struct nvkm_fifo *, const struct nvkm_oclass *, + void *, u32, struct nvkm_object **); + const struct nvkm_fifo_chan_oclass *chan[]; +}; + +void nv04_fifo_intr(struct nvkm_fifo *); +int nv04_fifo_engine_id(struct nvkm_fifo *, struct nvkm_engine *); +struct nvkm_engine *nv04_fifo_id_engine(struct nvkm_fifo *, int); +void nv04_fifo_pause(struct nvkm_fifo *, unsigned long *); +void nv04_fifo_start(struct nvkm_fifo *, unsigned long *); + +void gf100_fifo_intr_fault(struct nvkm_fifo *, int); + +int gk104_fifo_engine_id(struct nvkm_fifo *, struct nvkm_engine *); +struct nvkm_engine *gk104_fifo_id_engine(struct nvkm_fifo *, int); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/regsnv04.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/regsnv04.h new file mode 100644 index 000000000..4445a12b9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/regsnv04.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV04_FIFO_REGS_H__ +#define __NV04_FIFO_REGS_H__ + +#define NV04_PFIFO_DELAY_0 0x00002040 +#define NV04_PFIFO_DMA_TIMESLICE 0x00002044 +#define NV04_PFIFO_NEXT_CHANNEL 0x00002050 +#define NV03_PFIFO_INTR_0 0x00002100 +#define NV03_PFIFO_INTR_EN_0 0x00002140 +# define NV_PFIFO_INTR_CACHE_ERROR (1<<0) +# define NV_PFIFO_INTR_RUNOUT (1<<4) +# define NV_PFIFO_INTR_RUNOUT_OVERFLOW (1<<8) +# define NV_PFIFO_INTR_DMA_PUSHER (1<<12) +# define NV_PFIFO_INTR_DMA_PT (1<<16) +# define NV_PFIFO_INTR_SEMAPHORE (1<<20) +# define NV_PFIFO_INTR_ACQUIRE_TIMEOUT (1<<24) +#define NV03_PFIFO_RAMHT 0x00002210 +#define NV03_PFIFO_RAMFC 0x00002214 +#define NV03_PFIFO_RAMRO 0x00002218 +#define NV40_PFIFO_RAMFC 0x00002220 +#define NV03_PFIFO_CACHES 0x00002500 +#define NV04_PFIFO_MODE 0x00002504 +#define NV04_PFIFO_DMA 0x00002508 +#define NV04_PFIFO_SIZE 0x0000250c +#define NV50_PFIFO_CTX_TABLE(c) (0x2600+(c)*4) +#define NV50_PFIFO_CTX_TABLE__SIZE 128 +#define NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED (1<<31) +#define NV50_PFIFO_CTX_TABLE_UNK30_BAD (1<<30) +#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80 0x0FFFFFFF +#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84 0x00FFFFFF +#define NV03_PFIFO_CACHE0_PUSH0 0x00003000 +#define NV03_PFIFO_CACHE0_PULL0 0x00003040 +#define NV04_PFIFO_CACHE0_PULL0 0x00003050 +#define NV04_PFIFO_CACHE0_PULL1 0x00003054 +#define NV03_PFIFO_CACHE1_PUSH0 0x00003200 +#define NV03_PFIFO_CACHE1_PUSH1 0x00003204 +#define NV03_PFIFO_CACHE1_PUSH1_DMA (1<<8) +#define NV40_PFIFO_CACHE1_PUSH1_DMA (1<<16) +#define NV03_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000000f +#define NV10_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000001f +#define NV50_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000007f +#define NV03_PFIFO_CACHE1_PUT 0x00003210 +#define NV04_PFIFO_CACHE1_DMA_PUSH 0x00003220 +#define NV04_PFIFO_CACHE1_DMA_FETCH 0x00003224 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES 0x00000000 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES 0x00000008 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES 0x00000010 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES 0x00000018 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES 0x00000020 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES 0x00000028 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES 0x00000030 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES 0x00000038 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES 0x00000040 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES 0x00000048 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES 0x00000050 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES 0x00000058 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES 0x00000060 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES 0x00000068 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES 0x00000070 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES 0x00000078 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES 0x00000080 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES 0x00000088 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES 0x00000090 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES 0x00000098 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES 0x000000A0 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES 0x000000A8 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES 0x000000B0 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES 0x000000B8 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES 0x000000C0 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES 0x000000C8 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES 0x000000D0 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES 0x000000D8 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES 0x000000E0 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES 0x000000E8 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES 0x000000F0 +# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES 0x000000F8 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE 0x0000E000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES 0x00000000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES 0x00002000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES 0x00004000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES 0x00006000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES 0x00008000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES 0x0000A000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES 0x0000C000 +# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES 0x0000E000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 0x001F0000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_0 0x00000000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_1 0x00010000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_2 0x00020000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_3 0x00030000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_4 0x00040000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_5 0x00050000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_6 0x00060000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_7 0x00070000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 0x00080000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_9 0x00090000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_10 0x000A0000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_11 0x000B0000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_12 0x000C0000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_13 0x000D0000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_14 0x000E0000 +# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_15 0x000F0000 +# define NV_PFIFO_CACHE1_ENDIAN 0x80000000 +# define NV_PFIFO_CACHE1_LITTLE_ENDIAN 0x7FFFFFFF +# define NV_PFIFO_CACHE1_BIG_ENDIAN 0x80000000 +#define NV04_PFIFO_CACHE1_DMA_STATE 0x00003228 +#define NV04_PFIFO_CACHE1_DMA_INSTANCE 0x0000322c +#define NV04_PFIFO_CACHE1_DMA_CTL 0x00003230 +#define NV04_PFIFO_CACHE1_DMA_PUT 0x00003240 +#define NV04_PFIFO_CACHE1_DMA_GET 0x00003244 +#define NV10_PFIFO_CACHE1_REF_CNT 0x00003248 +#define NV10_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000324C +#define NV03_PFIFO_CACHE1_PULL0 0x00003240 +#define NV04_PFIFO_CACHE1_PULL0 0x00003250 +# define NV04_PFIFO_CACHE1_PULL0_HASH_FAILED 0x00000010 +# define NV04_PFIFO_CACHE1_PULL0_HASH_BUSY 0x00001000 +#define NV03_PFIFO_CACHE1_PULL1 0x00003250 +#define NV04_PFIFO_CACHE1_PULL1 0x00003254 +#define NV04_PFIFO_CACHE1_HASH 0x00003258 +#define NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT 0x00003260 +#define NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP 0x00003264 +#define NV10_PFIFO_CACHE1_ACQUIRE_VALUE 0x00003268 +#define NV10_PFIFO_CACHE1_SEMAPHORE 0x0000326C +#define NV03_PFIFO_CACHE1_GET 0x00003270 +#define NV04_PFIFO_CACHE1_ENGINE 0x00003280 +#define NV04_PFIFO_CACHE1_DMA_DCOUNT 0x000032A0 +#define NV40_PFIFO_GRCTX_INSTANCE 0x000032E0 +#define NV40_PFIFO_UNK32E4 0x000032E4 +#define NV04_PFIFO_CACHE1_METHOD(i) (0x00003800+(i*8)) +#define NV04_PFIFO_CACHE1_DATA(i) (0x00003804+(i*8)) +#define NV40_PFIFO_CACHE1_METHOD(i) (0x00090000+(i*8)) +#define NV40_PFIFO_CACHE1_DATA(i) (0x00090004+(i*8)) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/tu102.c new file mode 100644 index 000000000..260b197f8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/tu102.c @@ -0,0 +1,477 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gk104.h" +#include "cgrp.h" +#include "changk104.h" +#include "user.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +static void +tu102_fifo_runlist_commit(struct gk104_fifo *fifo, int runl, + struct nvkm_memory *mem, int nr) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + u64 addr = nvkm_memory_addr(mem); + /*XXX: target? */ + + nvkm_wr32(device, 0x002b00 + (runl * 0x10), lower_32_bits(addr)); + nvkm_wr32(device, 0x002b04 + (runl * 0x10), upper_32_bits(addr)); + nvkm_wr32(device, 0x002b08 + (runl * 0x10), nr); + + /*XXX: how to wait? can you even wait? */ +} + +static const struct gk104_fifo_runlist_func +tu102_fifo_runlist = { + .size = 16, + .cgrp = gv100_fifo_runlist_cgrp, + .chan = gv100_fifo_runlist_chan, + .commit = tu102_fifo_runlist_commit, +}; + +static const struct nvkm_enum +tu102_fifo_fault_engine[] = { + { 0x01, "DISPLAY" }, + { 0x03, "PTP" }, + { 0x06, "PWR_PMU" }, + { 0x08, "IFB", NULL, NVKM_ENGINE_IFB }, + { 0x09, "PERF" }, + { 0x1f, "PHYSICAL" }, + { 0x20, "HOST0" }, + { 0x21, "HOST1" }, + { 0x22, "HOST2" }, + { 0x23, "HOST3" }, + { 0x24, "HOST4" }, + { 0x25, "HOST5" }, + { 0x26, "HOST6" }, + { 0x27, "HOST7" }, + { 0x28, "HOST8" }, + { 0x29, "HOST9" }, + { 0x2a, "HOST10" }, + { 0x2b, "HOST11" }, + { 0x2c, "HOST12" }, + { 0x2d, "HOST13" }, + { 0x2e, "HOST14" }, + { 0x80, "BAR1", NULL, NVKM_SUBDEV_BAR }, + { 0xc0, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, + {} +}; + +static void +tu102_fifo_pbdma_init(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + const u32 mask = (1 << fifo->pbdma_nr) - 1; + /*XXX: this is a bit of a guess at this point in time. */ + nvkm_mask(device, 0xb65000, 0x80000fff, 0x80000000 | mask); +} + +static const struct gk104_fifo_pbdma_func +tu102_fifo_pbdma = { + .nr = gm200_fifo_pbdma_nr, + .init = tu102_fifo_pbdma_init, + .init_timeout = gk208_fifo_pbdma_init_timeout, +}; + +static const struct gk104_fifo_func +tu102_fifo = { + .pbdma = &tu102_fifo_pbdma, + .fault.access = gv100_fifo_fault_access, + .fault.engine = tu102_fifo_fault_engine, + .fault.reason = gv100_fifo_fault_reason, + .fault.hubclient = gv100_fifo_fault_hubclient, + .fault.gpcclient = gv100_fifo_fault_gpcclient, + .runlist = &tu102_fifo_runlist, + .user = {{-1,-1,VOLTA_USERMODE_A }, tu102_fifo_user_new }, + .chan = {{ 0, 0,TURING_CHANNEL_GPFIFO_A}, tu102_fifo_gpfifo_new }, + .cgrp_force = true, +}; + +static void +tu102_fifo_recover_work(struct work_struct *w) +{ + struct gk104_fifo *fifo = container_of(w, typeof(*fifo), recover.work); + struct nvkm_device *device = fifo->base.engine.subdev.device; + struct nvkm_engine *engine; + unsigned long flags; + u32 engm, runm, todo; + int engn, runl; + + spin_lock_irqsave(&fifo->base.lock, flags); + runm = fifo->recover.runm; + engm = fifo->recover.engm; + fifo->recover.engm = 0; + fifo->recover.runm = 0; + spin_unlock_irqrestore(&fifo->base.lock, flags); + + nvkm_mask(device, 0x002630, runm, runm); + + for (todo = engm; engn = __ffs(todo), todo; todo &= ~BIT(engn)) { + if ((engine = fifo->engine[engn].engine)) { + nvkm_subdev_fini(&engine->subdev, false); + WARN_ON(nvkm_subdev_init(&engine->subdev)); + } + } + + for (todo = runm; runl = __ffs(todo), todo; todo &= ~BIT(runl)) + gk104_fifo_runlist_update(fifo, runl); + + nvkm_mask(device, 0x002630, runm, 0x00000000); +} + +static void tu102_fifo_recover_engn(struct gk104_fifo *fifo, int engn); + +static void +tu102_fifo_recover_runl(struct gk104_fifo *fifo, int runl) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 runm = BIT(runl); + + assert_spin_locked(&fifo->base.lock); + if (fifo->recover.runm & runm) + return; + fifo->recover.runm |= runm; + + /* Block runlist to prevent channel assignment(s) from changing. */ + nvkm_mask(device, 0x002630, runm, runm); + + /* Schedule recovery. */ + nvkm_warn(subdev, "runlist %d: scheduled for recovery\n", runl); + schedule_work(&fifo->recover.work); +} + +static struct gk104_fifo_chan * +tu102_fifo_recover_chid(struct gk104_fifo *fifo, int runl, int chid) +{ + struct gk104_fifo_chan *chan; + struct nvkm_fifo_cgrp *cgrp; + + list_for_each_entry(chan, &fifo->runlist[runl].chan, head) { + if (chan->base.chid == chid) { + list_del_init(&chan->head); + return chan; + } + } + + list_for_each_entry(cgrp, &fifo->runlist[runl].cgrp, head) { + if (cgrp->id == chid) { + chan = list_first_entry(&cgrp->chan, typeof(*chan), head); + list_del_init(&chan->head); + if (!--cgrp->chan_nr) + list_del_init(&cgrp->head); + return chan; + } + } + + return NULL; +} + +static void +tu102_fifo_recover_chan(struct nvkm_fifo *base, int chid) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 stat = nvkm_rd32(device, 0x800004 + (chid * 0x08)); + const u32 runl = (stat & 0x000f0000) >> 16; + const bool used = (stat & 0x00000001); + unsigned long engn, engm = fifo->runlist[runl].engm; + struct gk104_fifo_chan *chan; + + assert_spin_locked(&fifo->base.lock); + if (!used) + return; + + /* Lookup SW state for channel, and mark it as dead. */ + chan = tu102_fifo_recover_chid(fifo, runl, chid); + if (chan) { + chan->killed = true; + nvkm_fifo_kevent(&fifo->base, chid); + } + + /* Disable channel. */ + nvkm_wr32(device, 0x800004 + (chid * 0x08), stat | 0x00000800); + nvkm_warn(subdev, "channel %d: killed\n", chid); + + /* Block channel assignments from changing during recovery. */ + tu102_fifo_recover_runl(fifo, runl); + + /* Schedule recovery for any engines the channel is on. */ + for_each_set_bit(engn, &engm, fifo->engine_nr) { + struct gk104_fifo_engine_status status; + + gk104_fifo_engine_status(fifo, engn, &status); + if (!status.chan || status.chan->id != chid) + continue; + tu102_fifo_recover_engn(fifo, engn); + } +} + +static void +tu102_fifo_recover_engn(struct gk104_fifo *fifo, int engn) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 runl = fifo->engine[engn].runl; + const u32 engm = BIT(engn); + struct gk104_fifo_engine_status status; + + assert_spin_locked(&fifo->base.lock); + if (fifo->recover.engm & engm) + return; + fifo->recover.engm |= engm; + + /* Block channel assignments from changing during recovery. */ + tu102_fifo_recover_runl(fifo, runl); + + /* Determine which channel (if any) is currently on the engine. */ + gk104_fifo_engine_status(fifo, engn, &status); + if (status.chan) { + /* The channel is not longer viable, kill it. */ + tu102_fifo_recover_chan(&fifo->base, status.chan->id); + } + + /* Preempt the runlist */ + nvkm_wr32(device, 0x2638, BIT(runl)); + + /* Schedule recovery. */ + nvkm_warn(subdev, "engine %d: scheduled for recovery\n", engn); + schedule_work(&fifo->recover.work); +} + +static void +tu102_fifo_fault(struct nvkm_fifo *base, struct nvkm_fault_data *info) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const struct nvkm_enum *er, *ee, *ec, *ea; + struct nvkm_engine *engine = NULL; + struct nvkm_fifo_chan *chan; + unsigned long flags; + const char *en = ""; + char ct[8] = "HUB/"; + int engn; + + er = nvkm_enum_find(fifo->func->fault.reason, info->reason); + ee = nvkm_enum_find(fifo->func->fault.engine, info->engine); + if (info->hub) { + ec = nvkm_enum_find(fifo->func->fault.hubclient, info->client); + } else { + ec = nvkm_enum_find(fifo->func->fault.gpcclient, info->client); + snprintf(ct, sizeof(ct), "GPC%d/", info->gpc); + } + ea = nvkm_enum_find(fifo->func->fault.access, info->access); + + if (ee && ee->data2) { + switch (ee->data2) { + case NVKM_SUBDEV_BAR: + nvkm_bar_bar1_reset(device); + break; + case NVKM_SUBDEV_INSTMEM: + nvkm_bar_bar2_reset(device); + break; + case NVKM_ENGINE_IFB: + nvkm_mask(device, 0x001718, 0x00000000, 0x00000000); + break; + default: + engine = nvkm_device_engine(device, ee->data2, 0); + break; + } + } + + if (ee == NULL) { + struct nvkm_subdev *subdev = nvkm_top_fault(device, info->engine); + if (subdev) { + if (subdev->func == &nvkm_engine) + engine = container_of(subdev, typeof(*engine), subdev); + en = engine->subdev.name; + } + } else { + en = ee->name; + } + + spin_lock_irqsave(&fifo->base.lock, flags); + chan = nvkm_fifo_chan_inst_locked(&fifo->base, info->inst); + + nvkm_error(subdev, + "fault %02x [%s] at %016llx engine %02x [%s] client %02x " + "[%s%s] reason %02x [%s] on channel %d [%010llx %s]\n", + info->access, ea ? ea->name : "", info->addr, + info->engine, ee ? ee->name : en, + info->client, ct, ec ? ec->name : "", + info->reason, er ? er->name : "", chan ? chan->chid : -1, + info->inst, chan ? chan->object.client->name : "unknown"); + + /* Kill the channel that caused the fault. */ + if (chan) + tu102_fifo_recover_chan(&fifo->base, chan->chid); + + /* Channel recovery will probably have already done this for the + * correct engine(s), but just in case we can't find the channel + * information... + */ + for (engn = 0; engn < fifo->engine_nr && engine; engn++) { + if (fifo->engine[engn].engine == engine) { + tu102_fifo_recover_engn(fifo, engn); + break; + } + } + + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +static void +tu102_fifo_intr_ctxsw_timeout(struct gk104_fifo *fifo) +{ + struct nvkm_device *device = fifo->base.engine.subdev.device; + unsigned long flags, engm; + u32 engn; + + spin_lock_irqsave(&fifo->base.lock, flags); + + engm = nvkm_rd32(device, 0x2a30); + nvkm_wr32(device, 0x2a30, engm); + + for_each_set_bit(engn, &engm, 32) + tu102_fifo_recover_engn(fifo, engn); + + spin_unlock_irqrestore(&fifo->base.lock, flags); +} + +static void +tu102_fifo_intr_sched(struct gk104_fifo *fifo) +{ + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 intr = nvkm_rd32(device, 0x00254c); + u32 code = intr & 0x000000ff; + + nvkm_error(subdev, "SCHED_ERROR %02x\n", code); +} + +static void +tu102_fifo_intr(struct nvkm_fifo *base) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mask = nvkm_rd32(device, 0x002140); + u32 stat = nvkm_rd32(device, 0x002100) & mask; + + if (stat & 0x00000001) { + gk104_fifo_intr_bind(fifo); + nvkm_wr32(device, 0x002100, 0x00000001); + stat &= ~0x00000001; + } + + if (stat & 0x00000002) { + tu102_fifo_intr_ctxsw_timeout(fifo); + stat &= ~0x00000002; + } + + if (stat & 0x00000100) { + tu102_fifo_intr_sched(fifo); + nvkm_wr32(device, 0x002100, 0x00000100); + stat &= ~0x00000100; + } + + if (stat & 0x00010000) { + gk104_fifo_intr_chsw(fifo); + nvkm_wr32(device, 0x002100, 0x00010000); + stat &= ~0x00010000; + } + + if (stat & 0x20000000) { + u32 mask = nvkm_rd32(device, 0x0025a0); + + while (mask) { + u32 unit = __ffs(mask); + + gk104_fifo_intr_pbdma_0(fifo, unit); + gk104_fifo_intr_pbdma_1(fifo, unit); + nvkm_wr32(device, 0x0025a0, (1 << unit)); + mask &= ~(1 << unit); + } + stat &= ~0x20000000; + } + + if (stat & 0x40000000) { + gk104_fifo_intr_runlist(fifo); + stat &= ~0x40000000; + } + + if (stat & 0x80000000) { + nvkm_wr32(device, 0x002100, 0x80000000); + gk104_fifo_intr_engine(fifo); + stat &= ~0x80000000; + } + + if (stat) { + nvkm_error(subdev, "INTR %08x\n", stat); + nvkm_mask(device, 0x002140, stat, 0x00000000); + nvkm_wr32(device, 0x002100, stat); + } +} + +static const struct nvkm_fifo_func +tu102_fifo_ = { + .dtor = gk104_fifo_dtor, + .oneinit = gk104_fifo_oneinit, + .info = gk104_fifo_info, + .init = gk104_fifo_init, + .fini = gk104_fifo_fini, + .intr = tu102_fifo_intr, + .fault = tu102_fifo_fault, + .engine_id = gk104_fifo_engine_id, + .id_engine = gk104_fifo_id_engine, + .uevent_init = gk104_fifo_uevent_init, + .uevent_fini = gk104_fifo_uevent_fini, + .recover_chan = tu102_fifo_recover_chan, + .class_get = gk104_fifo_class_get, + .class_new = gk104_fifo_class_new, +}; + +int +tu102_fifo_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fifo **pfifo) +{ + struct gk104_fifo *fifo; + + if (!(fifo = kzalloc(sizeof(*fifo), GFP_KERNEL))) + return -ENOMEM; + fifo->func = &tu102_fifo; + INIT_WORK(&fifo->recover.work, tu102_fifo_recover_work); + *pfifo = &fifo->base; + + return nvkm_fifo_ctor(&tu102_fifo_, device, type, inst, 4096, &fifo->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/user.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/user.h new file mode 100644 index 000000000..54a3a3092 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/user.h @@ -0,0 +1,8 @@ +#ifndef __NVKM_FIFO_USER_H__ +#define __NVKM_FIFO_USER_H__ +#include "priv.h" +int gv100_fifo_user_new(const struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +int tu102_fifo_user_new(const struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/usergv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/usergv100.c new file mode 100644 index 000000000..3dc3b8b31 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/usergv100.c @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "user.h" + +static int +gv100_fifo_user_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct nvkm_device *device = object->engine->subdev.device; + *addr = 0x810000 + device->func->resource_addr(device, 0); + *size = 0x010000; + *type = NVKM_OBJECT_MAP_IO; + return 0; +} + +static const struct nvkm_object_func +gv100_fifo_user = { + .map = gv100_fifo_user_map, +}; + +int +gv100_fifo_user_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + return nvkm_object_new_(&gv100_fifo_user, oclass, argv, argc, pobject); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/usertu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/usertu102.c new file mode 100644 index 000000000..217268f8c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/usertu102.c @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "user.h" + +static int +tu102_fifo_user_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct nvkm_device *device = object->engine->subdev.device; + *addr = 0xbb0000 + device->func->resource_addr(device, 0); + *size = 0x010000; + *type = NVKM_OBJECT_MAP_IO; + return 0; +} + +static const struct nvkm_object_func +tu102_fifo_user = { + .map = tu102_fifo_user_map, +}; + +int +tu102_fifo_user_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + return nvkm_object_new_(&tu102_fifo_user, oclass, argv, argc, pobject); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild new file mode 100644 index 000000000..558c86fd8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/gr/base.o +nvkm-y += nvkm/engine/gr/nv04.o +nvkm-y += nvkm/engine/gr/nv10.o +nvkm-y += nvkm/engine/gr/nv15.o +nvkm-y += nvkm/engine/gr/nv17.o +nvkm-y += nvkm/engine/gr/nv20.o +nvkm-y += nvkm/engine/gr/nv25.o +nvkm-y += nvkm/engine/gr/nv2a.o +nvkm-y += nvkm/engine/gr/nv30.o +nvkm-y += nvkm/engine/gr/nv34.o +nvkm-y += nvkm/engine/gr/nv35.o +nvkm-y += nvkm/engine/gr/nv40.o +nvkm-y += nvkm/engine/gr/nv44.o +nvkm-y += nvkm/engine/gr/nv50.o +nvkm-y += nvkm/engine/gr/g84.o +nvkm-y += nvkm/engine/gr/gt200.o +nvkm-y += nvkm/engine/gr/mcp79.o +nvkm-y += nvkm/engine/gr/gt215.o +nvkm-y += nvkm/engine/gr/mcp89.o +nvkm-y += nvkm/engine/gr/gf100.o +nvkm-y += nvkm/engine/gr/gf104.o +nvkm-y += nvkm/engine/gr/gf108.o +nvkm-y += nvkm/engine/gr/gf110.o +nvkm-y += nvkm/engine/gr/gf117.o +nvkm-y += nvkm/engine/gr/gf119.o +nvkm-y += nvkm/engine/gr/gk104.o +nvkm-y += nvkm/engine/gr/gk110.o +nvkm-y += nvkm/engine/gr/gk110b.o +nvkm-y += nvkm/engine/gr/gk208.o +nvkm-y += nvkm/engine/gr/gk20a.o +nvkm-y += nvkm/engine/gr/gm107.o +nvkm-y += nvkm/engine/gr/gm200.o +nvkm-y += nvkm/engine/gr/gm20b.o +nvkm-y += nvkm/engine/gr/gp100.o +nvkm-y += nvkm/engine/gr/gp102.o +nvkm-y += nvkm/engine/gr/gp104.o +nvkm-y += nvkm/engine/gr/gp107.o +nvkm-y += nvkm/engine/gr/gp108.o +nvkm-y += nvkm/engine/gr/gp10b.o +nvkm-y += nvkm/engine/gr/gv100.o +nvkm-y += nvkm/engine/gr/tu102.o + +nvkm-y += nvkm/engine/gr/ctxnv40.o +nvkm-y += nvkm/engine/gr/ctxnv50.o +nvkm-y += nvkm/engine/gr/ctxgf100.o +nvkm-y += nvkm/engine/gr/ctxgf104.o +nvkm-y += nvkm/engine/gr/ctxgf108.o +nvkm-y += nvkm/engine/gr/ctxgf110.o +nvkm-y += nvkm/engine/gr/ctxgf117.o +nvkm-y += nvkm/engine/gr/ctxgf119.o +nvkm-y += nvkm/engine/gr/ctxgk104.o +nvkm-y += nvkm/engine/gr/ctxgk110.o +nvkm-y += nvkm/engine/gr/ctxgk110b.o +nvkm-y += nvkm/engine/gr/ctxgk208.o +nvkm-y += nvkm/engine/gr/ctxgk20a.o +nvkm-y += nvkm/engine/gr/ctxgm107.o +nvkm-y += nvkm/engine/gr/ctxgm200.o +nvkm-y += nvkm/engine/gr/ctxgm20b.o +nvkm-y += nvkm/engine/gr/ctxgp100.o +nvkm-y += nvkm/engine/gr/ctxgp102.o +nvkm-y += nvkm/engine/gr/ctxgp104.o +nvkm-y += nvkm/engine/gr/ctxgp107.o +nvkm-y += nvkm/engine/gr/ctxgv100.o +nvkm-y += nvkm/engine/gr/ctxtu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c new file mode 100644 index 000000000..61759f544 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c @@ -0,0 +1,182 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +u32 +nvkm_gr_ctxsw_inst(struct nvkm_device *device) +{ + struct nvkm_gr *gr = device->gr; + if (gr && gr->func->ctxsw.inst) + return gr->func->ctxsw.inst(gr); + return 0; +} + +int +nvkm_gr_ctxsw_resume(struct nvkm_device *device) +{ + struct nvkm_gr *gr = device->gr; + if (gr && gr->func->ctxsw.resume) + return gr->func->ctxsw.resume(gr); + return 0; +} + +int +nvkm_gr_ctxsw_pause(struct nvkm_device *device) +{ + struct nvkm_gr *gr = device->gr; + if (gr && gr->func->ctxsw.pause) + return gr->func->ctxsw.pause(gr); + return 0; +} + +static bool +nvkm_gr_chsw_load(struct nvkm_engine *engine) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + if (gr->func->chsw_load) + return gr->func->chsw_load(gr); + return false; +} + +static void +nvkm_gr_tile(struct nvkm_engine *engine, int region, struct nvkm_fb_tile *tile) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + if (gr->func->tile) + gr->func->tile(gr, region, tile); +} + +u64 +nvkm_gr_units(struct nvkm_gr *gr) +{ + if (gr->func->units) + return gr->func->units(gr); + return 0; +} + +int +nvkm_gr_tlb_flush(struct nvkm_gr *gr) +{ + if (gr->func->tlb_flush) + return gr->func->tlb_flush(gr); + return -ENODEV; +} + +static int +nvkm_gr_oclass_get(struct nvkm_oclass *oclass, int index) +{ + struct nvkm_gr *gr = nvkm_gr(oclass->engine); + int c = 0; + + if (gr->func->object_get) { + int ret = gr->func->object_get(gr, index, &oclass->base); + if (oclass->base.oclass) + return index; + return ret; + } + + while (gr->func->sclass[c].oclass) { + if (c++ == index) { + oclass->base = gr->func->sclass[index]; + return index; + } + } + + return c; +} + +static int +nvkm_gr_cclass_new(struct nvkm_fifo_chan *chan, + const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct nvkm_gr *gr = nvkm_gr(oclass->engine); + if (gr->func->chan_new) + return gr->func->chan_new(gr, chan, oclass, pobject); + return 0; +} + +static void +nvkm_gr_intr(struct nvkm_engine *engine) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + gr->func->intr(gr); +} + +static int +nvkm_gr_oneinit(struct nvkm_engine *engine) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + if (gr->func->oneinit) + return gr->func->oneinit(gr); + return 0; +} + +static int +nvkm_gr_init(struct nvkm_engine *engine) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + return gr->func->init(gr); +} + +static int +nvkm_gr_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + if (gr->func->fini) + return gr->func->fini(gr, suspend); + return 0; +} + +static void * +nvkm_gr_dtor(struct nvkm_engine *engine) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + if (gr->func->dtor) + return gr->func->dtor(gr); + return gr; +} + +static const struct nvkm_engine_func +nvkm_gr = { + .dtor = nvkm_gr_dtor, + .oneinit = nvkm_gr_oneinit, + .init = nvkm_gr_init, + .fini = nvkm_gr_fini, + .intr = nvkm_gr_intr, + .tile = nvkm_gr_tile, + .chsw_load = nvkm_gr_chsw_load, + .fifo.cclass = nvkm_gr_cclass_new, + .fifo.sclass = nvkm_gr_oclass_get, +}; + +int +nvkm_gr_ctor(const struct nvkm_gr_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool enable, struct nvkm_gr *gr) +{ + gr->func = func; + return nvkm_engine_ctor(&nvkm_gr, device, type, inst, enable, &gr->engine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c new file mode 100644 index 000000000..297915719 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c @@ -0,0 +1,1603 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "ctxgf100.h" + +#include +#include +#include + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gf100_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +const struct gf100_gr_pack +gf100_grctx_pack_icmd[] = { + { gf100_grctx_init_icmd_0 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_9097_0[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x002700, 8, 0x20, 0x00000000 }, + { 0x002704, 8, 0x20, 0x00000000 }, + { 0x002708, 8, 0x20, 0x00000000 }, + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x002710, 8, 0x20, 0x00014000 }, + { 0x002714, 8, 0x20, 0x00000040 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002200, 5, 0x10, 0x00000022 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x00163c, 1, 0x04, 0x00000000 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x000dec, 1, 0x04, 0x00000001 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x0012ac, 1, 0x04, 0x00000001 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00001fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x003fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 4, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x0017bc, 2, 0x04, 0x00000000 }, + { 0x0017c4, 1, 0x04, 0x00000001 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x00075c, 1, 0x04, 0x00000003 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x000208, 1, 0x04, 0x00000000 }, + { 0x0002cc, 1, 0x04, 0x003fffff }, + { 0x0002d0, 1, 0x04, 0x00000c48 }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00300008 }, + { 0x001284, 1, 0x04, 0x04000080 }, + { 0x001450, 1, 0x04, 0x00300008 }, + { 0x001454, 1, 0x04, 0x04000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_902d_0[] = { + { 0x000200, 1, 0x04, 0x000000cf }, + { 0x000204, 1, 0x04, 0x00000001 }, + { 0x000208, 1, 0x04, 0x00000020 }, + { 0x00020c, 1, 0x04, 0x00000001 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000214, 1, 0x04, 0x00000080 }, + { 0x000218, 2, 0x04, 0x00000100 }, + { 0x000220, 2, 0x04, 0x00000000 }, + { 0x000230, 1, 0x04, 0x000000cf }, + { 0x000234, 1, 0x04, 0x00000001 }, + { 0x000238, 1, 0x04, 0x00000020 }, + { 0x00023c, 1, 0x04, 0x00000001 }, + { 0x000244, 1, 0x04, 0x00000080 }, + { 0x000248, 2, 0x04, 0x00000100 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_9039_0[] = { + { 0x00030c, 3, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000238, 2, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_90c0_0[] = { + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x000758, 1, 0x04, 0x00000100 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x000204, 3, 0x04, 0x00000000 }, + { 0x000214, 1, 0x04, 0x00000000 }, + { 0x00024c, 1, 0x04, 0x00000000 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gf100_grctx_pack_mthd[] = { + { gf100_grctx_init_9097_0, 0x9097 }, + { gf100_grctx_init_902d_0, 0x902d }, + { gf100_grctx_init_9039_0, 0x9039 }, + { gf100_grctx_init_90c0_0, 0x90c0 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_main_0[] = { + { 0x400204, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_fe_0[] = { + { 0x404004, 11, 0x04, 0x00000000 }, + { 0x404044, 1, 0x04, 0x00000000 }, + { 0x404094, 13, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf0000087 }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x404168, 1, 0x04, 0x00000000 }, + { 0x404174, 3, 0x04, 0x00000000 }, + { 0x404200, 8, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_pri_0[] = { + { 0x404404, 14, 0x04, 0x00000000 }, + { 0x404460, 2, 0x04, 0x00000000 }, + { 0x404468, 1, 0x04, 0x00ffffff }, + { 0x40446c, 1, 0x04, 0x00000000 }, + { 0x404480, 1, 0x04, 0x00000001 }, + { 0x404498, 1, 0x04, 0x00000001 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_memfmt_0[] = { + { 0x404604, 1, 0x04, 0x00000015 }, + { 0x404608, 1, 0x04, 0x00000000 }, + { 0x40460c, 1, 0x04, 0x00002e00 }, + { 0x404610, 1, 0x04, 0x00000100 }, + { 0x404618, 8, 0x04, 0x00000000 }, + { 0x404638, 1, 0x04, 0x00000004 }, + { 0x40463c, 8, 0x04, 0x00000000 }, + { 0x40465c, 1, 0x04, 0x007f0100 }, + { 0x404660, 7, 0x04, 0x00000000 }, + { 0x40467c, 1, 0x04, 0x00000002 }, + { 0x404680, 8, 0x04, 0x00000000 }, + { 0x4046a0, 1, 0x04, 0x007f0080 }, + { 0x4046a4, 18, 0x04, 0x00000000 }, + { 0x4046f0, 2, 0x04, 0x00000000 }, + { 0x404700, 13, 0x04, 0x00000000 }, + { 0x404734, 1, 0x04, 0x00000100 }, + { 0x404738, 8, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x078000bf }, + { 0x405830, 1, 0x04, 0x02180000 }, + { 0x405834, 2, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x000103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_rstr2d_0[] = { + { 0x407804, 1, 0x04, 0x00000023 }, + { 0x40780c, 1, 0x04, 0x0a418820 }, + { 0x407810, 1, 0x04, 0x062080e6 }, + { 0x407814, 1, 0x04, 0x020398a4 }, + { 0x407818, 1, 0x04, 0x0e629062 }, + { 0x40781c, 1, 0x04, 0x0a418820 }, + { 0x407820, 1, 0x04, 0x000000e6 }, + { 0x4078bc, 1, 0x04, 0x00000103 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_scc_0[] = { + { 0x408000, 2, 0x04, 0x00000000 }, + { 0x408008, 1, 0x04, 0x00000018 }, + { 0x40800c, 2, 0x04, 0x00000000 }, + { 0x408014, 1, 0x04, 0x00000069 }, + { 0x408018, 1, 0x04, 0xe100e100 }, + { 0x408064, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x0003e00d }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x02000001 }, + { 0x408908, 1, 0x04, 0x00c80929 }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +const struct gf100_gr_pack +gf100_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gf100_grctx_init_fe_0 }, + { gf100_grctx_init_pri_0 }, + { gf100_grctx_init_memfmt_0 }, + { gf100_grctx_init_ds_0 }, + { gf100_grctx_init_pd_0 }, + { gf100_grctx_init_rstr2d_0 }, + { gf100_grctx_init_scc_0 }, + { gf100_grctx_init_be_0 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_gpc_unk_0[] = { + { 0x418380, 1, 0x04, 0x00000016 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_prop_0[] = { + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x00200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_gpc_unk_1[] = { + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 1, 0x04, 0x00000000 }, + { 0x41870c, 1, 0x04, 0x07c80000 }, + { 0x418710, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x0006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x00000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x00100000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_zcull_0[] = { + { 0x41891c, 1, 0x04, 0x00ff00ff }, + { 0x418924, 1, 0x04, 0x00000000 }, + { 0x418928, 1, 0x04, 0x00ffff00 }, + { 0x41892c, 1, 0x04, 0x0000ff00 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_crstr_0[] = { + { 0x418b00, 1, 0x04, 0x00000000 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_gpm_0[] = { + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_gcc_0[] = { + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, + {} +}; + +const struct gf100_gr_pack +gf100_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gf100_grctx_init_prop_0 }, + { gf100_grctx_init_gpc_unk_1 }, + { gf100_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +const struct gf100_gr_pack +gf100_grctx_pack_gpc_1[] = { + { gf100_grctx_init_crstr_0 }, + { gf100_grctx_init_gpm_0 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_zcullr_0[] = { + { 0x418a00, 3, 0x04, 0x00000000 }, + { 0x418a0c, 1, 0x04, 0x00010000 }, + { 0x418a10, 3, 0x04, 0x00000000 }, + { 0x418a20, 3, 0x04, 0x00000000 }, + { 0x418a2c, 1, 0x04, 0x00010000 }, + { 0x418a30, 3, 0x04, 0x00000000 }, + { 0x418a40, 3, 0x04, 0x00000000 }, + { 0x418a4c, 1, 0x04, 0x00010000 }, + { 0x418a50, 3, 0x04, 0x00000000 }, + { 0x418a60, 3, 0x04, 0x00000000 }, + { 0x418a6c, 1, 0x04, 0x00010000 }, + { 0x418a70, 3, 0x04, 0x00000000 }, + { 0x418a80, 3, 0x04, 0x00000000 }, + { 0x418a8c, 1, 0x04, 0x00010000 }, + { 0x418a90, 3, 0x04, 0x00000000 }, + { 0x418aa0, 3, 0x04, 0x00000000 }, + { 0x418aac, 1, 0x04, 0x00010000 }, + { 0x418ab0, 3, 0x04, 0x00000000 }, + { 0x418ac0, 3, 0x04, 0x00000000 }, + { 0x418acc, 1, 0x04, 0x00010000 }, + { 0x418ad0, 3, 0x04, 0x00000000 }, + { 0x418ae0, 3, 0x04, 0x00000000 }, + { 0x418aec, 1, 0x04, 0x00010000 }, + { 0x418af0, 3, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gf100_grctx_pack_zcull[] = { + { gf100_grctx_init_zcullr_0 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_pe_0[] = { + { 0x419818, 1, 0x04, 0x00000000 }, + { 0x41983c, 1, 0x04, 0x00038bc7 }, + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x0000012a }, + { 0x419888, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_wwdx_0[] = { + { 0x419b00, 1, 0x04, 0x0a418820 }, + { 0x419b04, 1, 0x04, 0x062080e6 }, + { 0x419b08, 1, 0x04, 0x020398a4 }, + { 0x419b0c, 1, 0x04, 0x0e629062 }, + { 0x419b10, 1, 0x04, 0x0a418820 }, + { 0x419b14, 1, 0x04, 0x000000e6 }, + { 0x419bd0, 1, 0x04, 0x00900103 }, + { 0x419be0, 1, 0x04, 0x00000001 }, + { 0x419be4, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_mpc_0[] = { + { 0x419c00, 1, 0x04, 0x00000002 }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_l1c_0[] = { + { 0x419cb0, 1, 0x04, 0x00060048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + {} +}; + +const struct gf100_gr_init +gf100_grctx_init_tpccs_0[] = { + { 0x419d20, 1, 0x04, 0x02180000 }, + { 0x419d24, 1, 0x04, 0x00001fff }, + {} +}; + +static const struct gf100_gr_init +gf100_grctx_init_sm_0[] = { + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419f50, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gf100_grctx_pack_tpc[] = { + { gf100_grctx_init_pe_0 }, + { gf100_grctx_init_tex_0 }, + { gf100_grctx_init_wwdx_0 }, + { gf100_grctx_init_mpc_0 }, + { gf100_grctx_init_l1c_0 }, + { gf100_grctx_init_tpccs_0 }, + { gf100_grctx_init_sm_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +int +gf100_grctx_mmio_data(struct gf100_grctx *info, u32 size, u32 align, bool priv) +{ + if (info->data) { + info->buffer[info->buffer_nr] = round_up(info->addr, align); + info->addr = info->buffer[info->buffer_nr] + size; + info->data->size = size; + info->data->align = align; + info->data->priv = priv; + info->data++; + return info->buffer_nr++; + } + return -1; +} + +void +gf100_grctx_mmio_item(struct gf100_grctx *info, u32 addr, u32 data, + int shift, int buffer) +{ + struct nvkm_device *device = info->gr->base.engine.subdev.device; + if (info->data) { + if (shift >= 0) { + info->mmio->addr = addr; + info->mmio->data = data; + info->mmio->shift = shift; + info->mmio->buffer = buffer; + if (buffer >= 0) + data |= info->buffer[buffer] >> shift; + info->mmio++; + } else + return; + } else { + if (buffer >= 0) + return; + } + + nvkm_wr32(device, addr, data); +} + +void +gf100_grctx_generate_r419cb8(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419cb8, 0x00007c00, 0x00000000); +} + +void +gf100_grctx_generate_bundle(struct gf100_grctx *info) +{ + const struct gf100_grctx_func *grctx = info->gr->func->grctx; + const int s = 8; + const int b = mmio_vram(info, grctx->bundle_size, (1 << s), true); + mmio_refn(info, 0x408004, 0x00000000, s, b); + mmio_wr32(info, 0x408008, 0x80000000 | (grctx->bundle_size >> s)); + mmio_refn(info, 0x418808, 0x00000000, s, b); + mmio_wr32(info, 0x41880c, 0x80000000 | (grctx->bundle_size >> s)); +} + +void +gf100_grctx_generate_pagepool(struct gf100_grctx *info) +{ + const struct gf100_grctx_func *grctx = info->gr->func->grctx; + const int s = 8; + const int b = mmio_vram(info, grctx->pagepool_size, (1 << s), true); + mmio_refn(info, 0x40800c, 0x00000000, s, b); + mmio_wr32(info, 0x408010, 0x80000000); + mmio_refn(info, 0x419004, 0x00000000, s, b); + mmio_wr32(info, 0x419008, 0x00000000); +} + +void +gf100_grctx_generate_attrib(struct gf100_grctx *info) +{ + struct gf100_gr *gr = info->gr; + const struct gf100_grctx_func *grctx = gr->func->grctx; + const u32 attrib = grctx->attrib_nr; + const u32 size = 0x20 * (grctx->attrib_nr_max + grctx->alpha_nr_max); + const int s = 12; + const int b = mmio_vram(info, size * gr->tpc_total, (1 << s), false); + int gpc, tpc; + u32 bo = 0; + + mmio_refn(info, 0x418810, 0x80000000, s, b); + mmio_refn(info, 0x419848, 0x10000000, s, b); + mmio_wr32(info, 0x405830, (attrib << 16)); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (tpc = 0; tpc < gr->tpc_nr[gpc]; tpc++) { + const u32 o = TPC_UNIT(gpc, tpc, 0x0520); + mmio_skip(info, o, (attrib << 16) | ++bo); + mmio_wr32(info, o, (attrib << 16) | --bo); + bo += grctx->attrib_nr_max; + } + } +} + +void +gf100_grctx_generate_unkn(struct gf100_gr *gr) +{ +} + +void +gf100_grctx_generate_r4060a8(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u8 gpcmax = nvkm_rd32(device, 0x022430); + const u8 tpcmax = nvkm_rd32(device, 0x022434) * gpcmax; + int i, j, sm = 0; + u32 data; + + for (i = 0; i < DIV_ROUND_UP(tpcmax, 4); i++) { + for (data = 0, j = 0; j < 4; j++) { + if (sm < gr->sm_nr) + data |= gr->sm[sm++].gpc << (j * 8); + else + data |= 0x1f << (j * 8); + } + nvkm_wr32(device, 0x4060a8 + (i * 4), data); + } +} + +void +gf100_grctx_generate_rop_mapping(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 data[6] = {}, data2[2] = {}; + u8 shift, ntpcv; + int i; + + /* Pack tile map into register format. */ + for (i = 0; i < 32; i++) + data[i / 6] |= (gr->tile[i] & 0x07) << ((i % 6) * 5); + + /* Magic. */ + shift = 0; + ntpcv = gr->tpc_total; + while (!(ntpcv & (1 << 4))) { + ntpcv <<= 1; + shift++; + } + + data2[0] = (ntpcv << 16); + data2[0] |= (shift << 21); + data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24); + for (i = 1; i < 7; i++) + data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); + + /* GPC_BROADCAST */ + nvkm_wr32(device, 0x418bb8, (gr->tpc_total << 8) | + gr->screen_tile_row_offset); + for (i = 0; i < 6; i++) + nvkm_wr32(device, 0x418b08 + (i * 4), data[i]); + + /* GPC_BROADCAST.TP_BROADCAST */ + nvkm_wr32(device, 0x419bd0, (gr->tpc_total << 8) | + gr->screen_tile_row_offset | data2[0]); + nvkm_wr32(device, 0x419be4, data2[1]); + for (i = 0; i < 6; i++) + nvkm_wr32(device, 0x419b00 + (i * 4), data[i]); + + /* UNK78xx */ + nvkm_wr32(device, 0x4078bc, (gr->tpc_total << 8) | + gr->screen_tile_row_offset); + for (i = 0; i < 6; i++) + nvkm_wr32(device, 0x40780c + (i * 4), data[i]); +} + +void +gf100_grctx_generate_max_ways_evict(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 fbps = nvkm_rd32(device, 0x121c74); + if (fbps == 1) + nvkm_mask(device, 0x17e91c, 0x001f0000, 0x00090000); +} + +static const u32 +gf100_grctx_alpha_beta_map[17][32] = { + [1] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + [2] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + //XXX: 3 + [4] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, + }, + //XXX: 5 + //XXX: 6 + [7] = { + 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, + }, + [8] = { + 1, 1, 1, + 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, + 7, 7, 7, + }, + //XXX: 9 + //XXX: 10 + [11] = { + 1, 1, + 2, 2, 2, 2, + 3, 3, 3, + 4, 4, 4, 4, + 5, 5, 5, + 6, 6, 6, + 7, 7, 7, 7, + 8, 8, 8, + 9, 9, 9, 9, + 10, 10, + }, + //XXX: 12 + //XXX: 13 + [14] = { + 1, 1, + 2, 2, + 3, 3, 3, + 4, 4, 4, + 5, 5, + 6, 6, 6, + 7, 7, + 8, 8, 8, + 9, 9, + 10, 10, 10, + 11, 11, 11, + 12, 12, + 13, 13, + }, + [15] = { + 1, 1, + 2, 2, + 3, 3, + 4, 4, 4, + 5, 5, + 6, 6, 6, + 7, 7, + 8, 8, + 9, 9, 9, + 10, 10, + 11, 11, 11, + 12, 12, + 13, 13, + 14, 14, + }, + [16] = { + 1, 1, + 2, 2, + 3, 3, + 4, 4, + 5, 5, + 6, 6, 6, + 7, 7, + 8, 8, + 9, 9, + 10, 10, 10, + 11, 11, + 12, 12, + 13, 13, + 14, 14, + 15, 15, + }, +}; + +void +gf100_grctx_generate_alpha_beta_tables(struct gf100_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + int i, gpc; + + for (i = 0; i < 32; i++) { + u32 atarget = gf100_grctx_alpha_beta_map[gr->tpc_total][i]; + u32 abits[GPC_MAX] = {}, amask = 0, bmask = 0; + + if (!atarget) { + nvkm_warn(subdev, "missing alpha/beta mapping table\n"); + atarget = max_t(u32, gr->tpc_total * i / 32, 1); + } + + while (atarget) { + for (gpc = 0; atarget && gpc < gr->gpc_nr; gpc++) { + if (abits[gpc] < gr->tpc_nr[gpc]) { + abits[gpc]++; + atarget--; + } + } + } + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + u32 bbits = gr->tpc_nr[gpc] - abits[gpc]; + amask |= ((1 << abits[gpc]) - 1) << (gpc * 8); + bmask |= ((1 << bbits) - 1) << abits[gpc] << (gpc * 8); + } + + nvkm_wr32(device, 0x406800 + (i * 0x20), amask); + nvkm_wr32(device, 0x406c00 + (i * 0x20), bmask); + } +} + +void +gf100_grctx_generate_tpc_nr(struct gf100_gr *gr, int gpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, GPC_UNIT(gpc, 0x0c08), gr->tpc_nr[gpc]); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0c8c), gr->tpc_nr[gpc]); +} + +void +gf100_grctx_generate_sm_id(struct gf100_gr *gr, int gpc, int tpc, int sm) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x698), sm); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x4e8), sm); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0c10 + tpc * 4), sm); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x088), sm); +} + +void +gf100_grctx_generate_floorsweep(struct gf100_gr *gr) +{ + const struct gf100_grctx_func *func = gr->func->grctx; + int sm; + + for (sm = 0; sm < gr->sm_nr; sm++) { + func->sm_id(gr, gr->sm[sm].gpc, gr->sm[sm].tpc, sm); + if (func->tpc_nr) + func->tpc_nr(gr, gr->sm[sm].gpc); + } + + gf100_gr_init_num_tpc_per_gpc(gr, false, true); + if (!func->skip_pd_num_tpc_per_gpc) + gf100_gr_init_num_tpc_per_gpc(gr, true, false); + + if (func->r4060a8) + func->r4060a8(gr); + + func->rop_mapping(gr); + + if (func->alpha_beta_tables) + func->alpha_beta_tables(gr); + if (func->max_ways_evict) + func->max_ways_evict(gr); + if (func->dist_skip_table) + func->dist_skip_table(gr); + if (func->r406500) + func->r406500(gr); + if (func->gpc_tpc_nr) + func->gpc_tpc_nr(gr); + if (func->r419f78) + func->r419f78(gr); + if (func->tpc_mask) + func->tpc_mask(gr); + if (func->smid_config) + func->smid_config(gr); +} + +void +gf100_grctx_generate_main(struct gf100_gr *gr, struct gf100_grctx *info) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const struct gf100_grctx_func *grctx = gr->func->grctx; + u32 idle_timeout; + + nvkm_mc_unk260(device, 0); + + if (!gr->sw_ctx) { + gf100_gr_mmio(gr, grctx->hub); + gf100_gr_mmio(gr, grctx->gpc_0); + gf100_gr_mmio(gr, grctx->zcull); + gf100_gr_mmio(gr, grctx->gpc_1); + gf100_gr_mmio(gr, grctx->tpc); + gf100_gr_mmio(gr, grctx->ppc); + } else { + gf100_gr_mmio(gr, gr->sw_ctx); + } + + gf100_gr_wait_idle(gr); + + idle_timeout = nvkm_mask(device, 0x404154, 0xffffffff, 0x00000000); + + grctx->pagepool(info); + grctx->bundle(info); + grctx->attrib(info); + if (grctx->patch_ltc) + grctx->patch_ltc(info); + grctx->unkn(gr); + + gf100_grctx_generate_floorsweep(gr); + + gf100_gr_wait_idle(gr); + + if (grctx->r400088) grctx->r400088(gr, false); + if (gr->bundle) + gf100_gr_icmd(gr, gr->bundle); + else + gf100_gr_icmd(gr, grctx->icmd); + if (grctx->sw_veid_bundle_init) + gf100_gr_icmd(gr, grctx->sw_veid_bundle_init); + if (grctx->r400088) grctx->r400088(gr, true); + + nvkm_wr32(device, 0x404154, idle_timeout); + + if (gr->method) + gf100_gr_mthd(gr, gr->method); + else + gf100_gr_mthd(gr, grctx->mthd); + nvkm_mc_unk260(device, 1); + + if (grctx->r419cb8) + grctx->r419cb8(gr); + if (grctx->r418800) + grctx->r418800(gr); + if (grctx->r419eb0) + grctx->r419eb0(gr); + if (grctx->r419e00) + grctx->r419e00(gr); + if (grctx->r418e94) + grctx->r418e94(gr); + if (grctx->r419a3c) + grctx->r419a3c(gr); + if (grctx->r408840) + grctx->r408840(gr); + if (grctx->r419c0c) + grctx->r419c0c(gr); +} + +#define CB_RESERVED 0x80000 + +int +gf100_grctx_generate(struct gf100_gr *gr) +{ + const struct gf100_grctx_func *grctx = gr->func->grctx; + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_memory *inst = NULL; + struct nvkm_memory *data = NULL; + struct nvkm_vmm *vmm = NULL; + struct nvkm_vma *ctx = NULL; + struct gf100_grctx info; + int ret, i; + u64 addr; + + /* NV_PGRAPH_FE_PWR_MODE_FORCE_ON. */ + nvkm_wr32(device, 0x404170, 0x00000012); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x404170) & 0x00000010)) + break; + ); + + if (grctx->unkn88c) + grctx->unkn88c(gr, true); + + /* Reset FECS. */ + nvkm_wr32(device, 0x409614, 0x00000070); + nvkm_usec(device, 10, NVKM_DELAY); + nvkm_mask(device, 0x409614, 0x00000700, 0x00000700); + nvkm_usec(device, 10, NVKM_DELAY); + nvkm_rd32(device, 0x409614); + + if (grctx->unkn88c) + grctx->unkn88c(gr, false); + + /* NV_PGRAPH_FE_PWR_MODE_AUTO. */ + nvkm_wr32(device, 0x404170, 0x00000010); + + /* Init SCC RAM. */ + nvkm_wr32(device, 0x40802c, 0x00000001); + + /* Allocate memory to for a "channel", which we'll use to generate + * the default context values. + */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, + 0x1000, 0x1000, true, &inst); + if (ret) + goto done; + + ret = nvkm_vmm_new(device, 0, 0, NULL, 0, NULL, "grctx", &vmm); + if (ret) + goto done; + + vmm->debug = subdev->debug; + + ret = nvkm_vmm_join(vmm, inst); + if (ret) + goto done; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, + CB_RESERVED + gr->size, 0, true, &data); + if (ret) + goto done; + + ret = nvkm_vmm_get(vmm, 0, nvkm_memory_size(data), &ctx); + if (ret) + goto done; + + ret = nvkm_memory_map(data, 0, vmm, ctx, NULL, 0); + if (ret) + goto done; + + + /* Setup context pointer. */ + nvkm_kmap(inst); + nvkm_wo32(inst, 0x0210, lower_32_bits(ctx->addr + CB_RESERVED) | 4); + nvkm_wo32(inst, 0x0214, upper_32_bits(ctx->addr + CB_RESERVED)); + nvkm_done(inst); + + /* Setup default state for mmio list construction. */ + info.gr = gr; + info.data = gr->mmio_data; + info.mmio = gr->mmio_list; + info.addr = ctx->addr; + info.buffer_nr = 0; + + /* Make channel current. */ + addr = nvkm_memory_addr(inst) >> 12; + if (gr->firmware) { + ret = gf100_gr_fecs_bind_pointer(gr, 0x80000000 | addr); + if (ret) + goto done; + + nvkm_kmap(data); + nvkm_wo32(data, 0x1c, 1); + nvkm_wo32(data, 0x20, 0); + nvkm_wo32(data, 0x28, 0); + nvkm_wo32(data, 0x2c, 0); + nvkm_done(data); + } else { + nvkm_wr32(device, 0x409840, 0x80000000); + nvkm_wr32(device, 0x409500, 0x80000000 | addr); + nvkm_wr32(device, 0x409504, 0x00000001); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800) & 0x80000000) + break; + ); + } + + grctx->main(gr, &info); + + /* Trigger a context unload by unsetting the "next channel valid" bit + * and faking a context switch interrupt. + */ + nvkm_mask(device, 0x409b04, 0x80000000, 0x00000000); + nvkm_wr32(device, 0x409000, 0x00000100); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x409b00) & 0x80000000)) + break; + ) < 0) { + ret = -EBUSY; + goto done; + } + + gr->data = kmalloc(gr->size, GFP_KERNEL); + if (gr->data) { + nvkm_kmap(data); + for (i = 0; i < gr->size; i += 4) + gr->data[i / 4] = nvkm_ro32(data, CB_RESERVED + i); + nvkm_done(data); + ret = 0; + } else { + ret = -ENOMEM; + } + +done: + nvkm_vmm_put(vmm, &ctx); + nvkm_memory_unref(&data); + nvkm_vmm_part(vmm, inst); + nvkm_vmm_unref(&vmm); + nvkm_memory_unref(&inst); + return ret; +} + +const struct gf100_grctx_func +gf100_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gf100_grctx_generate_unkn, + .hub = gf100_grctx_pack_hub, + .gpc_0 = gf100_grctx_pack_gpc_0, + .gpc_1 = gf100_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gf100_grctx_pack_tpc, + .icmd = gf100_grctx_pack_icmd, + .mthd = gf100_grctx_pack_mthd, + .bundle = gf100_grctx_generate_bundle, + .bundle_size = 0x1800, + .pagepool = gf100_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf100_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .r4060a8 = gf100_grctx_generate_r4060a8, + .rop_mapping = gf100_grctx_generate_rop_mapping, + .alpha_beta_tables = gf100_grctx_generate_alpha_beta_tables, + .max_ways_evict = gf100_grctx_generate_max_ways_evict, + .r419cb8 = gf100_grctx_generate_r419cb8, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h new file mode 100644 index 000000000..679aff79f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_GRCTX_NVC0_H__ +#define __NVKM_GRCTX_NVC0_H__ +#include "gf100.h" + +struct gf100_grctx { + struct gf100_gr *gr; + struct gf100_gr_data *data; + struct gf100_gr_mmio *mmio; + int buffer_nr; + u64 buffer[4]; + u64 addr; +}; + +int gf100_grctx_mmio_data(struct gf100_grctx *, u32 size, u32 align, bool priv); +void gf100_grctx_mmio_item(struct gf100_grctx *, u32 addr, u32 data, int s, int); + +#define mmio_vram(a,b,c,d) gf100_grctx_mmio_data((a), (b), (c), (d)) +#define mmio_refn(a,b,c,d,e) gf100_grctx_mmio_item((a), (b), (c), (d), (e)) +#define mmio_skip(a,b,c) mmio_refn((a), (b), (c), -1, -1) +#define mmio_wr32(a,b,c) mmio_refn((a), (b), (c), 0, -1) + +struct gf100_grctx_func { + void (*unkn88c)(struct gf100_gr *, bool on); + /* main context generation function */ + void (*main)(struct gf100_gr *, struct gf100_grctx *); + /* context-specific modify-on-first-load list generation function */ + void (*unkn)(struct gf100_gr *); + /* mmio context data */ + const struct gf100_gr_pack *hub; + const struct gf100_gr_pack *gpc_0; + const struct gf100_gr_pack *gpc_1; + const struct gf100_gr_pack *zcull; + const struct gf100_gr_pack *tpc; + const struct gf100_gr_pack *ppc; + /* indirect context data, generated with icmds/mthds */ + const struct gf100_gr_pack *icmd; + const struct gf100_gr_pack *mthd; + const struct gf100_gr_pack *sw_veid_bundle_init; + /* bundle circular buffer */ + void (*bundle)(struct gf100_grctx *); + u32 bundle_size; + u32 bundle_min_gpm_fifo_depth; + u32 bundle_token_limit; + /* pagepool */ + void (*pagepool)(struct gf100_grctx *); + u32 pagepool_size; + /* attribute(/alpha) circular buffer */ + void (*attrib)(struct gf100_grctx *); + u32 attrib_nr_max; + u32 attrib_nr; + u32 alpha_nr_max; + u32 alpha_nr; + u32 gfxp_nr; + /* other patch buffer stuff */ + void (*patch_ltc)(struct gf100_grctx *); + /* floorsweeping */ + void (*sm_id)(struct gf100_gr *, int gpc, int tpc, int sm); + void (*tpc_nr)(struct gf100_gr *, int gpc); + bool skip_pd_num_tpc_per_gpc; + void (*r4060a8)(struct gf100_gr *); + void (*rop_mapping)(struct gf100_gr *); + void (*alpha_beta_tables)(struct gf100_gr *); + void (*max_ways_evict)(struct gf100_gr *); + void (*dist_skip_table)(struct gf100_gr *); + void (*r406500)(struct gf100_gr *); + void (*gpc_tpc_nr)(struct gf100_gr *); + void (*r419f78)(struct gf100_gr *); + void (*tpc_mask)(struct gf100_gr *); + void (*smid_config)(struct gf100_gr *); + /* misc other things */ + void (*r400088)(struct gf100_gr *, bool); + void (*r419cb8)(struct gf100_gr *); + void (*r418800)(struct gf100_gr *); + void (*r419eb0)(struct gf100_gr *); + void (*r419e00)(struct gf100_gr *); + void (*r418e94)(struct gf100_gr *); + void (*r419a3c)(struct gf100_gr *); + void (*r408840)(struct gf100_gr *); + void (*r419c0c)(struct gf100_gr *); +}; + +extern const struct gf100_grctx_func gf100_grctx; +int gf100_grctx_generate(struct gf100_gr *); +void gf100_grctx_generate_main(struct gf100_gr *, struct gf100_grctx *); +void gf100_grctx_generate_bundle(struct gf100_grctx *); +void gf100_grctx_generate_pagepool(struct gf100_grctx *); +void gf100_grctx_generate_attrib(struct gf100_grctx *); +void gf100_grctx_generate_unkn(struct gf100_gr *); +void gf100_grctx_generate_floorsweep(struct gf100_gr *); +void gf100_grctx_generate_sm_id(struct gf100_gr *, int, int, int); +void gf100_grctx_generate_tpc_nr(struct gf100_gr *, int); +void gf100_grctx_generate_r4060a8(struct gf100_gr *); +void gf100_grctx_generate_rop_mapping(struct gf100_gr *); +void gf100_grctx_generate_alpha_beta_tables(struct gf100_gr *); +void gf100_grctx_generate_max_ways_evict(struct gf100_gr *); +void gf100_grctx_generate_r419cb8(struct gf100_gr *); + +extern const struct gf100_grctx_func gf108_grctx; +void gf108_grctx_generate_attrib(struct gf100_grctx *); +void gf108_grctx_generate_unkn(struct gf100_gr *); + +extern const struct gf100_grctx_func gf104_grctx; +extern const struct gf100_grctx_func gf110_grctx; + +extern const struct gf100_grctx_func gf117_grctx; +void gf117_grctx_generate_attrib(struct gf100_grctx *); +void gf117_grctx_generate_rop_mapping(struct gf100_gr *); +void gf117_grctx_generate_dist_skip_table(struct gf100_gr *); + +extern const struct gf100_grctx_func gf119_grctx; + +extern const struct gf100_grctx_func gk104_grctx; +void gk104_grctx_generate_alpha_beta_tables(struct gf100_gr *); +void gk104_grctx_generate_gpc_tpc_nr(struct gf100_gr *); + +extern const struct gf100_grctx_func gk20a_grctx; +void gk104_grctx_generate_bundle(struct gf100_grctx *); +void gk104_grctx_generate_pagepool(struct gf100_grctx *); +void gk104_grctx_generate_patch_ltc(struct gf100_grctx *); +void gk104_grctx_generate_unkn(struct gf100_gr *); +void gk104_grctx_generate_r418800(struct gf100_gr *); + +extern const struct gf100_grctx_func gk110_grctx; +void gk110_grctx_generate_r419eb0(struct gf100_gr *); +void gk110_grctx_generate_r419f78(struct gf100_gr *); + +extern const struct gf100_grctx_func gk110b_grctx; +extern const struct gf100_grctx_func gk208_grctx; + +extern const struct gf100_grctx_func gm107_grctx; +void gm107_grctx_generate_bundle(struct gf100_grctx *); +void gm107_grctx_generate_pagepool(struct gf100_grctx *); +void gm107_grctx_generate_attrib(struct gf100_grctx *); +void gm107_grctx_generate_sm_id(struct gf100_gr *, int, int, int); + +extern const struct gf100_grctx_func gm200_grctx; +void gm200_grctx_generate_dist_skip_table(struct gf100_gr *); +void gm200_grctx_generate_r406500(struct gf100_gr *); +void gm200_grctx_generate_tpc_mask(struct gf100_gr *); +void gm200_grctx_generate_smid_config(struct gf100_gr *); +void gm200_grctx_generate_r419a3c(struct gf100_gr *); + +extern const struct gf100_grctx_func gm20b_grctx; + +extern const struct gf100_grctx_func gp100_grctx; +void gp100_grctx_generate_pagepool(struct gf100_grctx *); +void gp100_grctx_generate_smid_config(struct gf100_gr *); + +extern const struct gf100_grctx_func gp102_grctx; +void gp102_grctx_generate_attrib(struct gf100_grctx *); + +extern const struct gf100_grctx_func gp104_grctx; + +extern const struct gf100_grctx_func gp107_grctx; + +extern const struct gf100_grctx_func gv100_grctx; + +extern const struct gf100_grctx_func tu102_grctx; +void gv100_grctx_unkn88c(struct gf100_gr *, bool); +void gv100_grctx_generate_unkn(struct gf100_gr *); +extern const struct gf100_gr_init gv100_grctx_init_sw_veid_bundle_init_0[]; +void gv100_grctx_generate_attrib(struct gf100_grctx *); +void gv100_grctx_generate_rop_mapping(struct gf100_gr *); +void gv100_grctx_generate_r400088(struct gf100_gr *, bool); + +/* context init value lists */ + +extern const struct gf100_gr_pack gf100_grctx_pack_icmd[]; + +extern const struct gf100_gr_pack gf100_grctx_pack_mthd[]; +extern const struct gf100_gr_init gf100_grctx_init_902d_0[]; +extern const struct gf100_gr_init gf100_grctx_init_9039_0[]; +extern const struct gf100_gr_init gf100_grctx_init_90c0_0[]; + +extern const struct gf100_gr_pack gf100_grctx_pack_hub[]; +extern const struct gf100_gr_init gf100_grctx_init_main_0[]; +extern const struct gf100_gr_init gf100_grctx_init_fe_0[]; +extern const struct gf100_gr_init gf100_grctx_init_pri_0[]; +extern const struct gf100_gr_init gf100_grctx_init_memfmt_0[]; +extern const struct gf100_gr_init gf100_grctx_init_rstr2d_0[]; +extern const struct gf100_gr_init gf100_grctx_init_scc_0[]; + +extern const struct gf100_gr_pack gf100_grctx_pack_gpc_0[]; +extern const struct gf100_gr_pack gf100_grctx_pack_gpc_1[]; +extern const struct gf100_gr_init gf100_grctx_init_gpc_unk_0[]; +extern const struct gf100_gr_init gf100_grctx_init_prop_0[]; +extern const struct gf100_gr_init gf100_grctx_init_gpc_unk_1[]; +extern const struct gf100_gr_init gf100_grctx_init_zcull_0[]; +extern const struct gf100_gr_init gf100_grctx_init_crstr_0[]; +extern const struct gf100_gr_init gf100_grctx_init_gpm_0[]; +extern const struct gf100_gr_init gf100_grctx_init_gcc_0[]; + +extern const struct gf100_gr_pack gf100_grctx_pack_zcull[]; + +extern const struct gf100_gr_pack gf100_grctx_pack_tpc[]; +extern const struct gf100_gr_init gf100_grctx_init_pe_0[]; +extern const struct gf100_gr_init gf100_grctx_init_wwdx_0[]; +extern const struct gf100_gr_init gf100_grctx_init_mpc_0[]; +extern const struct gf100_gr_init gf100_grctx_init_tpccs_0[]; + +extern const struct gf100_gr_init gf104_grctx_init_tex_0[]; +extern const struct gf100_gr_init gf104_grctx_init_l1c_0[]; +extern const struct gf100_gr_init gf104_grctx_init_sm_0[]; + +extern const struct gf100_gr_init gf108_grctx_init_9097_0[]; + +extern const struct gf100_gr_init gf108_grctx_init_gpm_0[]; + +extern const struct gf100_gr_init gf108_grctx_init_pe_0[]; +extern const struct gf100_gr_init gf108_grctx_init_wwdx_0[]; +extern const struct gf100_gr_init gf108_grctx_init_tpccs_0[]; + +extern const struct gf100_gr_init gf110_grctx_init_9197_0[]; +extern const struct gf100_gr_init gf110_grctx_init_9297_0[]; + +extern const struct gf100_gr_pack gf119_grctx_pack_icmd[]; + +extern const struct gf100_gr_pack gf119_grctx_pack_mthd[]; + +extern const struct gf100_gr_init gf119_grctx_init_fe_0[]; +extern const struct gf100_gr_init gf119_grctx_init_be_0[]; + +extern const struct gf100_gr_init gf119_grctx_init_prop_0[]; +extern const struct gf100_gr_init gf119_grctx_init_gpc_unk_1[]; +extern const struct gf100_gr_init gf119_grctx_init_crstr_0[]; + +extern const struct gf100_gr_init gf119_grctx_init_sm_0[]; + +extern const struct gf100_gr_init gf117_grctx_init_pe_0[]; + +extern const struct gf100_gr_init gf117_grctx_init_wwdx_0[]; + +extern const struct gf100_gr_pack gf117_grctx_pack_gpc_1[]; + +extern const struct gf100_gr_init gk104_grctx_init_memfmt_0[]; +extern const struct gf100_gr_init gk104_grctx_init_ds_0[]; +extern const struct gf100_gr_init gk104_grctx_init_scc_0[]; + +extern const struct gf100_gr_init gk104_grctx_init_gpm_0[]; + +extern const struct gf100_gr_init gk104_grctx_init_pes_0[]; + +extern const struct gf100_gr_pack gk104_grctx_pack_hub[]; +extern const struct gf100_gr_pack gk104_grctx_pack_tpc[]; +extern const struct gf100_gr_pack gk104_grctx_pack_ppc[]; +extern const struct gf100_gr_pack gk104_grctx_pack_icmd[]; +extern const struct gf100_gr_init gk104_grctx_init_a097_0[]; + +extern const struct gf100_gr_pack gk110_grctx_pack_icmd[]; + +extern const struct gf100_gr_pack gk110_grctx_pack_mthd[]; + +extern const struct gf100_gr_pack gk110_grctx_pack_hub[]; +extern const struct gf100_gr_init gk110_grctx_init_pri_0[]; +extern const struct gf100_gr_init gk110_grctx_init_cwd_0[]; + +extern const struct gf100_gr_pack gk110_grctx_pack_gpc_0[]; +extern const struct gf100_gr_pack gk110_grctx_pack_gpc_1[]; +extern const struct gf100_gr_init gk110_grctx_init_gpc_unk_2[]; + +extern const struct gf100_gr_init gk110_grctx_init_tex_0[]; +extern const struct gf100_gr_init gk110_grctx_init_mpc_0[]; +extern const struct gf100_gr_init gk110_grctx_init_l1c_0[]; + +extern const struct gf100_gr_pack gk110_grctx_pack_ppc[]; + +extern const struct gf100_gr_init gk208_grctx_init_rstr2d_0[]; + +extern const struct gf100_gr_init gk208_grctx_init_prop_0[]; +extern const struct gf100_gr_init gk208_grctx_init_crstr_0[]; + +extern const struct gf100_gr_init gm107_grctx_init_gpc_unk_0[]; +extern const struct gf100_gr_init gm107_grctx_init_wwdx_0[]; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c new file mode 100644 index 000000000..7a0564b6e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c @@ -0,0 +1,107 @@ +/* + * 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 "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +const struct gf100_gr_init +gf104_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00000000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419ac4, 1, 0x04, 0x0007f440 }, + {} +}; + +const struct gf100_gr_init +gf104_grctx_init_l1c_0[] = { + { 0x419cb0, 1, 0x04, 0x00020048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + {} +}; + +const struct gf100_gr_init +gf104_grctx_init_sm_0[] = { + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00011110 }, + { 0x419f30, 11, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf104_grctx_pack_tpc[] = { + { gf100_grctx_init_pe_0 }, + { gf104_grctx_init_tex_0 }, + { gf100_grctx_init_wwdx_0 }, + { gf100_grctx_init_mpc_0 }, + { gf104_grctx_init_l1c_0 }, + { gf100_grctx_init_tpccs_0 }, + { gf104_grctx_init_sm_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +const struct gf100_grctx_func +gf104_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gf100_grctx_generate_unkn, + .hub = gf100_grctx_pack_hub, + .gpc_0 = gf100_grctx_pack_gpc_0, + .gpc_1 = gf100_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gf104_grctx_pack_tpc, + .icmd = gf100_grctx_pack_icmd, + .mthd = gf100_grctx_pack_mthd, + .bundle = gf100_grctx_generate_bundle, + .bundle_size = 0x1800, + .pagepool = gf100_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf100_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .r4060a8 = gf100_grctx_generate_r4060a8, + .rop_mapping = gf100_grctx_generate_rop_mapping, + .alpha_beta_tables = gf100_grctx_generate_alpha_beta_tables, + .max_ways_evict = gf100_grctx_generate_max_ways_evict, + .r419cb8 = gf100_grctx_generate_r419cb8, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c new file mode 100644 index 000000000..dda2c32e6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c @@ -0,0 +1,810 @@ +/* + * 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 "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gf108_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +gf108_grctx_pack_icmd[] = { + { gf108_grctx_init_icmd_0 }, + {} +}; + +const struct gf100_gr_init +gf108_grctx_init_9097_0[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x002700, 8, 0x20, 0x00000000 }, + { 0x002704, 8, 0x20, 0x00000000 }, + { 0x002708, 8, 0x20, 0x00000000 }, + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x002710, 8, 0x20, 0x00014000 }, + { 0x002714, 8, 0x20, 0x00000040 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002200, 5, 0x10, 0x00000022 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x00163c, 1, 0x04, 0x00000000 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x000dec, 1, 0x04, 0x00000001 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x0012ac, 1, 0x04, 0x00000001 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00001fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x003fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 4, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x0017bc, 2, 0x04, 0x00000000 }, + { 0x0017c4, 1, 0x04, 0x00000001 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x00075c, 1, 0x04, 0x00000003 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x000208, 1, 0x04, 0x00000000 }, + { 0x0002cc, 1, 0x04, 0x003fffff }, + { 0x0002d0, 1, 0x04, 0x00000c48 }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00300008 }, + { 0x001284, 1, 0x04, 0x04000080 }, + { 0x001450, 1, 0x04, 0x00300008 }, + { 0x001454, 1, 0x04, 0x04000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf108_grctx_init_9197_0[] = { + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x0002e4, 1, 0x04, 0x0000b001 }, + {} +}; + +static const struct gf100_gr_pack +gf108_grctx_pack_mthd[] = { + { gf108_grctx_init_9097_0, 0x9097 }, + { gf108_grctx_init_9197_0, 0x9197 }, + { gf100_grctx_init_902d_0, 0x902d }, + { gf100_grctx_init_9039_0, 0x9039 }, + { gf100_grctx_init_90c0_0, 0x90c0 }, + {} +}; + +static const struct gf100_gr_init +gf108_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180218 }, + { 0x405834, 2, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf108_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x000103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 2, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x80140078 }, + { 0x4064c4, 1, 0x04, 0x0086ffff }, + {} +}; + +static const struct gf100_gr_init +gf108_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1003e005 }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x00c80929 }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +static const struct gf100_gr_pack +gf108_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gf100_grctx_init_fe_0 }, + { gf100_grctx_init_pri_0 }, + { gf100_grctx_init_memfmt_0 }, + { gf108_grctx_init_ds_0 }, + { gf108_grctx_init_pd_0 }, + { gf100_grctx_init_rstr2d_0 }, + { gf100_grctx_init_scc_0 }, + { gf108_grctx_init_be_0 }, + {} +}; + +static const struct gf100_gr_init +gf108_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x0006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x00100018 }, + {} +}; + +const struct gf100_gr_init +gf108_grctx_init_gpm_0[] = { + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +gf108_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gf100_grctx_init_prop_0 }, + { gf100_grctx_init_gpc_unk_1 }, + { gf108_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +static const struct gf100_gr_pack +gf108_grctx_pack_gpc_1[] = { + { gf100_grctx_init_crstr_0 }, + { gf108_grctx_init_gpm_0 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +const struct gf100_gr_init +gf108_grctx_init_pe_0[] = { + { 0x419818, 1, 0x04, 0x00000000 }, + { 0x41983c, 1, 0x04, 0x00038bc7 }, + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000129 }, + { 0x419888, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf108_grctx_init_wwdx_0[] = { + { 0x419b00, 1, 0x04, 0x0a418820 }, + { 0x419b04, 1, 0x04, 0x062080e6 }, + { 0x419b08, 1, 0x04, 0x020398a4 }, + { 0x419b0c, 1, 0x04, 0x0e629062 }, + { 0x419b10, 1, 0x04, 0x0a418820 }, + { 0x419b14, 1, 0x04, 0x000000e6 }, + { 0x419bd0, 1, 0x04, 0x00900103 }, + { 0x419be0, 1, 0x04, 0x00400001 }, + { 0x419be4, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf108_grctx_init_tpccs_0[] = { + { 0x419d20, 1, 0x04, 0x12180000 }, + { 0x419d24, 1, 0x04, 0x00001fff }, + { 0x419d44, 1, 0x04, 0x02180218 }, + {} +}; + +static const struct gf100_gr_pack +gf108_grctx_pack_tpc[] = { + { gf108_grctx_init_pe_0 }, + { gf104_grctx_init_tex_0 }, + { gf108_grctx_init_wwdx_0 }, + { gf100_grctx_init_mpc_0 }, + { gf104_grctx_init_l1c_0 }, + { gf108_grctx_init_tpccs_0 }, + { gf104_grctx_init_sm_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +void +gf108_grctx_generate_attrib(struct gf100_grctx *info) +{ + struct gf100_gr *gr = info->gr; + const struct gf100_grctx_func *grctx = gr->func->grctx; + const u32 alpha = grctx->alpha_nr; + const u32 beta = grctx->attrib_nr; + const u32 size = 0x20 * (grctx->attrib_nr_max + grctx->alpha_nr_max); + const int s = 12; + const int b = mmio_vram(info, size * gr->tpc_total, (1 << s), false); + const int timeslice_mode = 1; + const int max_batches = 0xffff; + u32 bo = 0; + u32 ao = bo + grctx->attrib_nr_max * gr->tpc_total; + int gpc, tpc; + + mmio_refn(info, 0x418810, 0x80000000, s, b); + mmio_refn(info, 0x419848, 0x10000000, s, b); + mmio_wr32(info, 0x405830, (beta << 16) | alpha); + mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (tpc = 0; tpc < gr->tpc_nr[gpc]; tpc++) { + const u32 a = alpha; + const u32 b = beta; + const u32 t = timeslice_mode; + const u32 o = TPC_UNIT(gpc, tpc, 0x500); + mmio_skip(info, o + 0x20, (t << 28) | (b << 16) | ++bo); + mmio_wr32(info, o + 0x20, (t << 28) | (b << 16) | --bo); + bo += grctx->attrib_nr_max; + mmio_wr32(info, o + 0x44, (a << 16) | ao); + ao += grctx->alpha_nr_max; + } + } +} + +void +gf108_grctx_generate_unkn(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x418c6c, 0x00000001, 0x00000001); + nvkm_mask(device, 0x41980c, 0x00000010, 0x00000010); + nvkm_mask(device, 0x419814, 0x00000004, 0x00000004); + nvkm_mask(device, 0x4064c0, 0x80000000, 0x80000000); + nvkm_mask(device, 0x405800, 0x08000000, 0x08000000); + nvkm_mask(device, 0x419c00, 0x00000008, 0x00000008); +} + +const struct gf100_grctx_func +gf108_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gf108_grctx_generate_unkn, + .hub = gf108_grctx_pack_hub, + .gpc_0 = gf108_grctx_pack_gpc_0, + .gpc_1 = gf108_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gf108_grctx_pack_tpc, + .icmd = gf108_grctx_pack_icmd, + .mthd = gf108_grctx_pack_mthd, + .bundle = gf100_grctx_generate_bundle, + .bundle_size = 0x1800, + .pagepool = gf100_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf108_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .alpha_nr_max = 0x324, + .alpha_nr = 0x218, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .r4060a8 = gf100_grctx_generate_r4060a8, + .rop_mapping = gf100_grctx_generate_rop_mapping, + .alpha_beta_tables = gf100_grctx_generate_alpha_beta_tables, + .max_ways_evict = gf100_grctx_generate_max_ways_evict, + .r419cb8 = gf100_grctx_generate_r419cb8, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c new file mode 100644 index 000000000..f5cca5e6a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c @@ -0,0 +1,355 @@ +/* + * 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 "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gf110_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +gf110_grctx_pack_icmd[] = { + { gf110_grctx_init_icmd_0 }, + {} +}; + +const struct gf100_gr_init +gf110_grctx_init_9197_0[] = { + { 0x0002e4, 1, 0x04, 0x0000b001 }, + {} +}; + +const struct gf100_gr_init +gf110_grctx_init_9297_0[] = { + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00036c, 2, 0x04, 0x00000000 }, + { 0x0007a4, 2, 0x04, 0x00000000 }, + { 0x000374, 1, 0x04, 0x00000000 }, + { 0x000378, 1, 0x04, 0x00000020 }, + {} +}; + +static const struct gf100_gr_pack +gf110_grctx_pack_mthd[] = { + { gf108_grctx_init_9097_0, 0x9097 }, + { gf110_grctx_init_9197_0, 0x9197 }, + { gf110_grctx_init_9297_0, 0x9297 }, + { gf100_grctx_init_902d_0, 0x902d }, + { gf100_grctx_init_9039_0, 0x9039 }, + { gf100_grctx_init_90c0_0, 0x90c0 }, + {} +}; + +static const struct gf100_gr_init +gf110_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x0006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x00000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100000 }, + {} +}; + +static const struct gf100_gr_pack +gf110_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gf100_grctx_init_prop_0 }, + { gf100_grctx_init_gpc_unk_1 }, + { gf110_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +const struct gf100_grctx_func +gf110_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gf100_grctx_generate_unkn, + .hub = gf100_grctx_pack_hub, + .gpc_0 = gf110_grctx_pack_gpc_0, + .gpc_1 = gf100_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gf100_grctx_pack_tpc, + .icmd = gf110_grctx_pack_icmd, + .mthd = gf110_grctx_pack_mthd, + .bundle = gf100_grctx_generate_bundle, + .bundle_size = 0x1800, + .pagepool = gf100_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf100_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .r4060a8 = gf100_grctx_generate_r4060a8, + .rop_mapping = gf100_grctx_generate_rop_mapping, + .alpha_beta_tables = gf100_grctx_generate_alpha_beta_tables, + .max_ways_evict = gf100_grctx_generate_max_ways_evict, + .r419cb8 = gf100_grctx_generate_r419cb8, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c new file mode 100644 index 000000000..276c282d1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c @@ -0,0 +1,310 @@ +/* + * 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 "ctxgf100.h" + +#include +#include + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gf117_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180324 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf117_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x000103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x801a0078 }, + { 0x4064c4, 1, 0x04, 0x00c9ffff }, + { 0x4064d0, 8, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf117_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gf119_grctx_init_fe_0 }, + { gf100_grctx_init_pri_0 }, + { gf100_grctx_init_memfmt_0 }, + { gf117_grctx_init_ds_0 }, + { gf117_grctx_init_pd_0 }, + { gf100_grctx_init_rstr2d_0 }, + { gf100_grctx_init_scc_0 }, + { gf119_grctx_init_be_0 }, + {} +}; + +static const struct gf100_gr_init +gf117_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100018 }, + {} +}; + +static const struct gf100_gr_pack +gf117_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gf119_grctx_init_prop_0 }, + { gf119_grctx_init_gpc_unk_1 }, + { gf117_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +const struct gf100_gr_pack +gf117_grctx_pack_gpc_1[] = { + { gf119_grctx_init_crstr_0 }, + { gf108_grctx_init_gpm_0 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +const struct gf100_gr_init +gf117_grctx_init_pe_0[] = { + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000129 }, + { 0x419888, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf117_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00008000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419ac4, 1, 0x04, 0x0017f440 }, + {} +}; + +static const struct gf100_gr_init +gf117_grctx_init_mpc_0[] = { + { 0x419c00, 1, 0x04, 0x0000000a }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + {} +}; + +static const struct gf100_gr_pack +gf117_grctx_pack_tpc[] = { + { gf117_grctx_init_pe_0 }, + { gf117_grctx_init_tex_0 }, + { gf117_grctx_init_mpc_0 }, + { gf104_grctx_init_l1c_0 }, + { gf119_grctx_init_sm_0 }, + {} +}; + +static const struct gf100_gr_init +gf117_grctx_init_pes_0[] = { + { 0x41be24, 1, 0x04, 0x00000002 }, + {} +}; + +static const struct gf100_gr_init +gf117_grctx_init_cbm_0[] = { + { 0x41bec0, 1, 0x04, 0x12180000 }, + { 0x41bec4, 1, 0x04, 0x00003fff }, + { 0x41bee4, 1, 0x04, 0x03240218 }, + {} +}; + +const struct gf100_gr_init +gf117_grctx_init_wwdx_0[] = { + { 0x41bf00, 1, 0x04, 0x0a418820 }, + { 0x41bf04, 1, 0x04, 0x062080e6 }, + { 0x41bf08, 1, 0x04, 0x020398a4 }, + { 0x41bf0c, 1, 0x04, 0x0e629062 }, + { 0x41bf10, 1, 0x04, 0x0a418820 }, + { 0x41bf14, 1, 0x04, 0x000000e6 }, + { 0x41bfd0, 1, 0x04, 0x00900103 }, + { 0x41bfe0, 1, 0x04, 0x00400001 }, + { 0x41bfe4, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf117_grctx_pack_ppc[] = { + { gf117_grctx_init_pes_0 }, + { gf117_grctx_init_cbm_0 }, + { gf117_grctx_init_wwdx_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +void +gf117_grctx_generate_dist_skip_table(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int i; + + for (i = 0; i < 8; i++) + nvkm_wr32(device, 0x4064d0 + (i * 0x04), 0x00000000); +} + +void +gf117_grctx_generate_rop_mapping(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 data[6] = {}, data2[2] = {}; + u8 shift, ntpcv; + int i; + + /* Pack tile map into register format. */ + for (i = 0; i < 32; i++) + data[i / 6] |= (gr->tile[i] & 0x07) << ((i % 6) * 5); + + /* Magic. */ + shift = 0; + ntpcv = gr->tpc_total; + while (!(ntpcv & (1 << 4))) { + ntpcv <<= 1; + shift++; + } + + data2[0] = (ntpcv << 16); + data2[0] |= (shift << 21); + data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24); + for (i = 1; i < 7; i++) + data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); + + /* GPC_BROADCAST */ + nvkm_wr32(device, 0x418bb8, (gr->tpc_total << 8) | + gr->screen_tile_row_offset); + for (i = 0; i < 6; i++) + nvkm_wr32(device, 0x418b08 + (i * 4), data[i]); + + /* GPC_BROADCAST.TP_BROADCAST */ + nvkm_wr32(device, 0x41bfd0, (gr->tpc_total << 8) | + gr->screen_tile_row_offset | data2[0]); + nvkm_wr32(device, 0x41bfe4, data2[1]); + for (i = 0; i < 6; i++) + nvkm_wr32(device, 0x41bf00 + (i * 4), data[i]); + + /* UNK78xx */ + nvkm_wr32(device, 0x4078bc, (gr->tpc_total << 8) | + gr->screen_tile_row_offset); + for (i = 0; i < 6; i++) + nvkm_wr32(device, 0x40780c + (i * 4), data[i]); +} + +void +gf117_grctx_generate_attrib(struct gf100_grctx *info) +{ + struct gf100_gr *gr = info->gr; + const struct gf100_grctx_func *grctx = gr->func->grctx; + const u32 alpha = grctx->alpha_nr; + const u32 beta = grctx->attrib_nr; + const u32 size = 0x20 * (grctx->attrib_nr_max + grctx->alpha_nr_max); + const int s = 12; + const int b = mmio_vram(info, size * gr->tpc_total, (1 << s), false); + const int timeslice_mode = 1; + const int max_batches = 0xffff; + u32 bo = 0; + u32 ao = bo + grctx->attrib_nr_max * gr->tpc_total; + int gpc, ppc; + + mmio_refn(info, 0x418810, 0x80000000, s, b); + mmio_refn(info, 0x419848, 0x10000000, s, b); + mmio_wr32(info, 0x405830, (beta << 16) | alpha); + mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++) { + const u32 a = alpha * gr->ppc_tpc_nr[gpc][ppc]; + const u32 b = beta * gr->ppc_tpc_nr[gpc][ppc]; + const u32 t = timeslice_mode; + const u32 o = PPC_UNIT(gpc, ppc, 0); + if (!(gr->ppc_mask[gpc] & (1 << ppc))) + continue; + mmio_skip(info, o + 0xc0, (t << 28) | (b << 16) | ++bo); + mmio_wr32(info, o + 0xc0, (t << 28) | (b << 16) | --bo); + bo += grctx->attrib_nr_max * gr->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, o + 0xe4, (a << 16) | ao); + ao += grctx->alpha_nr_max * gr->ppc_tpc_nr[gpc][ppc]; + } + } +} + +const struct gf100_grctx_func +gf117_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gf117_grctx_pack_hub, + .gpc_0 = gf117_grctx_pack_gpc_0, + .gpc_1 = gf117_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gf117_grctx_pack_tpc, + .ppc = gf117_grctx_pack_ppc, + .icmd = gf119_grctx_pack_icmd, + .mthd = gf119_grctx_pack_mthd, + .bundle = gf100_grctx_generate_bundle, + .bundle_size = 0x1800, + .pagepool = gf100_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf117_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .alpha_nr_max = 0x7ff, + .alpha_nr = 0x324, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .r4060a8 = gf100_grctx_generate_r4060a8, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .alpha_beta_tables = gf100_grctx_generate_alpha_beta_tables, + .max_ways_evict = gf100_grctx_generate_max_ways_evict, + .dist_skip_table = gf117_grctx_generate_dist_skip_table, + .r419cb8 = gf100_grctx_generate_r419cb8, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c new file mode 100644 index 000000000..0cfe46366 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c @@ -0,0 +1,525 @@ +/* + * 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 "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gf119_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000400, 24, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x000440, 24, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +const struct gf100_gr_pack +gf119_grctx_pack_icmd[] = { + { gf119_grctx_init_icmd_0 }, + {} +}; + +static const struct gf100_gr_init +gf119_grctx_init_90c0_0[] = { + { 0x002700, 8, 0x20, 0x00000000 }, + { 0x002704, 8, 0x20, 0x00000000 }, + { 0x002708, 8, 0x20, 0x00000000 }, + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x002710, 8, 0x20, 0x00014000 }, + { 0x002714, 8, 0x20, 0x00000040 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x000758, 1, 0x04, 0x00000100 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x000204, 3, 0x04, 0x00000000 }, + { 0x000214, 1, 0x04, 0x00000000 }, + { 0x00024c, 1, 0x04, 0x00000000 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gf119_grctx_pack_mthd[] = { + { gf108_grctx_init_9097_0, 0x9097 }, + { gf110_grctx_init_9197_0, 0x9197 }, + { gf110_grctx_init_9297_0, 0x9297 }, + { gf100_grctx_init_902d_0, 0x902d }, + { gf100_grctx_init_9039_0, 0x9039 }, + { gf119_grctx_init_90c0_0, 0x90c0 }, + {} +}; + +const struct gf100_gr_init +gf119_grctx_init_fe_0[] = { + { 0x404004, 10, 0x04, 0x00000000 }, + { 0x404044, 1, 0x04, 0x00000000 }, + { 0x404094, 13, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf0000087 }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x404168, 1, 0x04, 0x00000000 }, + { 0x404178, 2, 0x04, 0x00000000 }, + { 0x404200, 8, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf119_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180218 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf119_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x000103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x80140078 }, + { 0x4064c4, 1, 0x04, 0x0086ffff }, + {} +}; + +const struct gf100_gr_init +gf119_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1043e005 }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x00c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +static const struct gf100_gr_pack +gf119_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gf119_grctx_init_fe_0 }, + { gf100_grctx_init_pri_0 }, + { gf100_grctx_init_memfmt_0 }, + { gf119_grctx_init_ds_0 }, + { gf119_grctx_init_pd_0 }, + { gf100_grctx_init_rstr2d_0 }, + { gf100_grctx_init_scc_0 }, + { gf119_grctx_init_be_0 }, + {} +}; + +const struct gf100_gr_init +gf119_grctx_init_prop_0[] = { + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x02200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_grctx_init_gpc_unk_1[] = { + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 3, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf119_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100008 }, + {} +}; + +const struct gf100_gr_init +gf119_grctx_init_crstr_0[] = { + { 0x418b00, 1, 0x04, 0x00000006 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + {} +}; + +static const struct gf100_gr_pack +gf119_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gf119_grctx_init_prop_0 }, + { gf119_grctx_init_gpc_unk_1 }, + { gf119_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +static const struct gf100_gr_init +gf119_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00000000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419ac4, 1, 0x04, 0x0017f440 }, + {} +}; + +static const struct gf100_gr_init +gf119_grctx_init_mpc_0[] = { + { 0x419c00, 1, 0x04, 0x0000000a }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3cf3cf3c }, + {} +}; + +const struct gf100_gr_init +gf119_grctx_init_sm_0[] = { + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00010110 }, + { 0x419f30, 11, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf119_grctx_pack_tpc[] = { + { gf108_grctx_init_pe_0 }, + { gf119_grctx_init_tex_0 }, + { gf108_grctx_init_wwdx_0 }, + { gf119_grctx_init_mpc_0 }, + { gf104_grctx_init_l1c_0 }, + { gf108_grctx_init_tpccs_0 }, + { gf119_grctx_init_sm_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +const struct gf100_grctx_func +gf119_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gf108_grctx_generate_unkn, + .hub = gf119_grctx_pack_hub, + .gpc_0 = gf119_grctx_pack_gpc_0, + .gpc_1 = gf117_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gf119_grctx_pack_tpc, + .icmd = gf119_grctx_pack_icmd, + .mthd = gf119_grctx_pack_mthd, + .bundle = gf100_grctx_generate_bundle, + .bundle_size = 0x1800, + .pagepool = gf100_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf108_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .alpha_nr_max = 0x324, + .alpha_nr = 0x218, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .r4060a8 = gf100_grctx_generate_r4060a8, + .rop_mapping = gf100_grctx_generate_rop_mapping, + .alpha_beta_tables = gf100_grctx_generate_alpha_beta_tables, + .max_ways_evict = gf100_grctx_generate_max_ways_evict, + .r419cb8 = gf100_grctx_generate_r419cb8, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c new file mode 100644 index 000000000..f894f8254 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c @@ -0,0 +1,1010 @@ +/* + * 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 "ctxgf100.h" + +#include +#include + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gk104_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000383, 1, 0x01, 0x00000011 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x003fffff }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00400008 }, + { 0x000841, 1, 0x01, 0x08000080 }, + { 0x000842, 1, 0x01, 0x00400008 }, + { 0x000843, 1, 0x01, 0x08000080 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000008 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +const struct gf100_gr_pack +gk104_grctx_pack_icmd[] = { + { gk104_grctx_init_icmd_0 }, + {} +}; + +const struct gf100_gr_init +gk104_grctx_init_a097_0[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x00163c, 1, 0x04, 0x00000000 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x000dec, 1, 0x04, 0x00000001 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x0012ac, 1, 0x04, 0x00000001 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00000fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x000fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00260c, 1, 0x04, 0x00000000 }, + { 0x0007ac, 1, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x0002e4, 1, 0x04, 0x0000b001 }, + { 0x00036c, 2, 0x04, 0x00000000 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 4, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x0007a4, 2, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x000374, 1, 0x04, 0x00000000 }, + { 0x000378, 1, 0x04, 0x00000020 }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x0017bc, 2, 0x04, 0x00000000 }, + { 0x0017c4, 1, 0x04, 0x00000001 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x00075c, 1, 0x04, 0x00000003 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x000208, 1, 0x04, 0x00000000 }, + { 0x0002cc, 2, 0x04, 0x003fffff }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00400008 }, + { 0x001284, 1, 0x04, 0x08000080 }, + { 0x001450, 1, 0x04, 0x00400008 }, + { 0x001454, 1, 0x04, 0x08000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gk104_grctx_pack_mthd[] = { + { gk104_grctx_init_a097_0, 0xa097 }, + { gf100_grctx_init_902d_0, 0x902d }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_fe_0[] = { + { 0x404010, 5, 0x04, 0x00000000 }, + { 0x404024, 1, 0x04, 0x0000e000 }, + { 0x404028, 1, 0x04, 0x00000000 }, + { 0x4040a8, 8, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf800008f }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x4041a0, 4, 0x04, 0x00000000 }, + { 0x404200, 4, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk104_grctx_init_memfmt_0[] = { + { 0x404604, 1, 0x04, 0x00000014 }, + { 0x404608, 1, 0x04, 0x00000000 }, + { 0x40460c, 1, 0x04, 0x00003fff }, + { 0x404610, 1, 0x04, 0x00000100 }, + { 0x404618, 4, 0x04, 0x00000000 }, + { 0x40462c, 2, 0x04, 0x00000000 }, + { 0x404640, 1, 0x04, 0x00000000 }, + { 0x404654, 1, 0x04, 0x00000000 }, + { 0x404660, 1, 0x04, 0x00000000 }, + { 0x404678, 1, 0x04, 0x00000000 }, + { 0x40467c, 1, 0x04, 0x00000002 }, + { 0x404680, 8, 0x04, 0x00000000 }, + { 0x4046a0, 1, 0x04, 0x007f0080 }, + { 0x4046a4, 8, 0x04, 0x00000000 }, + { 0x4046c8, 3, 0x04, 0x00000000 }, + { 0x404700, 3, 0x04, 0x00000000 }, + { 0x404718, 7, 0x04, 0x00000000 }, + { 0x404734, 1, 0x04, 0x00000100 }, + { 0x404738, 2, 0x04, 0x00000000 }, + { 0x404744, 2, 0x04, 0x00000000 }, + { 0x404754, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk104_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180648 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_cwd_0[] = { + { 0x405b00, 1, 0x04, 0x00000000 }, + { 0x405b10, 1, 0x04, 0x00001000 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x004103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 2, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x801a00f0 }, + { 0x4064c4, 1, 0x04, 0x0192ffff }, + { 0x4064c8, 1, 0x04, 0x01800600 }, + { 0x4064cc, 9, 0x04, 0x00000000 }, + { 0x4064fc, 1, 0x04, 0x0000022a }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_sked_0[] = { + { 0x407040, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk104_grctx_init_scc_0[] = { + { 0x408000, 2, 0x04, 0x00000000 }, + { 0x408008, 1, 0x04, 0x00000030 }, + { 0x40800c, 2, 0x04, 0x00000000 }, + { 0x408014, 1, 0x04, 0x00000069 }, + { 0x408018, 1, 0x04, 0xe100e100 }, + { 0x408064, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1043e005 }, + { 0x408840, 1, 0x04, 0x0000000b }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x00c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +const struct gf100_gr_pack +gk104_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gk104_grctx_init_fe_0 }, + { gf100_grctx_init_pri_0 }, + { gk104_grctx_init_memfmt_0 }, + { gk104_grctx_init_ds_0 }, + { gk104_grctx_init_cwd_0 }, + { gk104_grctx_init_pd_0 }, + { gk104_grctx_init_sked_0 }, + { gf100_grctx_init_rstr2d_0 }, + { gk104_grctx_init_scc_0 }, + { gk104_grctx_init_be_0 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00000044 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100018 }, + {} +}; + +const struct gf100_gr_init +gk104_grctx_init_gpm_0[] = { + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c40, 1, 0x04, 0xffffffff }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +gk104_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gf119_grctx_init_prop_0 }, + { gf119_grctx_init_gpc_unk_1 }, + { gk104_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +static const struct gf100_gr_pack +gk104_grctx_pack_gpc_1[] = { + { gf119_grctx_init_crstr_0 }, + { gk104_grctx_init_gpm_0 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000000f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000021 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x0000c000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419a30, 1, 0x04, 0x00000001 }, + { 0x419ac4, 1, 0x04, 0x0037f440 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_mpc_0[] = { + { 0x419c00, 1, 0x04, 0x0000000a }, + { 0x419c04, 1, 0x04, 0x80000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_l1c_0[] = { + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00003203 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_sm_0[] = { + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000402 }, + { 0x419e44, 1, 0x04, 0x0013eff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000007f }, + { 0x419e50, 19, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x00001f8f }, + { 0x419eb0, 1, 0x04, 0x00000d3f }, + { 0x419ec8, 1, 0x04, 0x0001304f }, + { 0x419f30, 8, 0x04, 0x00000000 }, + { 0x419f58, 1, 0x04, 0x00000000 }, + { 0x419f70, 1, 0x04, 0x00000000 }, + { 0x419f78, 1, 0x04, 0x0000000b }, + { 0x419f7c, 1, 0x04, 0x0000027c }, + {} +}; + +const struct gf100_gr_pack +gk104_grctx_pack_tpc[] = { + { gf117_grctx_init_pe_0 }, + { gk104_grctx_init_tex_0 }, + { gk104_grctx_init_mpc_0 }, + { gk104_grctx_init_l1c_0 }, + { gk104_grctx_init_sm_0 }, + {} +}; + +const struct gf100_gr_init +gk104_grctx_init_pes_0[] = { + { 0x41be24, 1, 0x04, 0x00000006 }, + {} +}; + +static const struct gf100_gr_init +gk104_grctx_init_cbm_0[] = { + { 0x41bec0, 1, 0x04, 0x12180000 }, + { 0x41bec4, 1, 0x04, 0x00037f7f }, + { 0x41bee4, 1, 0x04, 0x06480430 }, + {} +}; + +const struct gf100_gr_pack +gk104_grctx_pack_ppc[] = { + { gk104_grctx_init_pes_0 }, + { gk104_grctx_init_cbm_0 }, + { gf117_grctx_init_wwdx_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +void +gk104_grctx_generate_r418800(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + /*XXX: Not real sure where to apply these, there doesn't seem + * to be any pattern to which chipsets it's done on. + * + * Perhaps a VBIOS tweak? + */ + if (0) { + nvkm_mask(device, 0x418800, 0x00200000, 0x00200000); + nvkm_mask(device, 0x41be10, 0x00800000, 0x00800000); + } +} + +void +gk104_grctx_generate_patch_ltc(struct gf100_grctx *info) +{ + struct nvkm_device *device = info->gr->base.engine.subdev.device; + u32 data0 = nvkm_rd32(device, 0x17e91c); + u32 data1 = nvkm_rd32(device, 0x17e920); + /*XXX: Figure out how to modify this correctly! */ + mmio_wr32(info, 0x17e91c, data0); + mmio_wr32(info, 0x17e920, data1); +} + +void +gk104_grctx_generate_bundle(struct gf100_grctx *info) +{ + const struct gf100_grctx_func *grctx = info->gr->func->grctx; + const u32 state_limit = min(grctx->bundle_min_gpm_fifo_depth, + grctx->bundle_size / 0x20); + const u32 token_limit = grctx->bundle_token_limit; + const int s = 8; + const int b = mmio_vram(info, grctx->bundle_size, (1 << s), true); + mmio_refn(info, 0x408004, 0x00000000, s, b); + mmio_wr32(info, 0x408008, 0x80000000 | (grctx->bundle_size >> s)); + mmio_refn(info, 0x418808, 0x00000000, s, b); + mmio_wr32(info, 0x41880c, 0x80000000 | (grctx->bundle_size >> s)); + mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit); +} + +void +gk104_grctx_generate_pagepool(struct gf100_grctx *info) +{ + const struct gf100_grctx_func *grctx = info->gr->func->grctx; + const int s = 8; + const int b = mmio_vram(info, grctx->pagepool_size, (1 << s), true); + mmio_refn(info, 0x40800c, 0x00000000, s, b); + mmio_wr32(info, 0x408010, 0x80000000); + mmio_refn(info, 0x419004, 0x00000000, s, b); + mmio_wr32(info, 0x419008, 0x00000000); + mmio_wr32(info, 0x4064cc, 0x80000000); +} + +void +gk104_grctx_generate_unkn(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x418c6c, 0x00000001, 0x00000001); + nvkm_mask(device, 0x41980c, 0x00000010, 0x00000010); + nvkm_mask(device, 0x41be08, 0x00000004, 0x00000004); + nvkm_mask(device, 0x4064c0, 0x80000000, 0x80000000); + nvkm_mask(device, 0x405800, 0x08000000, 0x08000000); + nvkm_mask(device, 0x419c00, 0x00000008, 0x00000008); +} + +static void +gk104_grctx_generate_r419f78(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + /* bit 3 set disables loads in fp helper invocations, we need it enabled */ + nvkm_mask(device, 0x419f78, 0x00000009, 0x00000000); +} + +void +gk104_grctx_generate_gpc_tpc_nr(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, 0x405b00, (gr->tpc_total << 8) | gr->gpc_nr); +} + +void +gk104_grctx_generate_alpha_beta_tables(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int i, j, gpc, ppc; + + for (i = 0; i < 32; i++) { + u32 atarget = max_t(u32, gr->tpc_total * i / 32, 1); + u32 btarget = gr->tpc_total - atarget; + bool alpha = atarget < btarget; + u64 amask = 0, bmask = 0; + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->func->ppc_nr; ppc++) { + u32 ppc_tpcs = gr->ppc_tpc_nr[gpc][ppc]; + u32 abits, bbits, pmask; + + if (alpha) { + abits = atarget ? ppc_tpcs : 0; + bbits = ppc_tpcs - abits; + } else { + bbits = btarget ? ppc_tpcs : 0; + abits = ppc_tpcs - bbits; + } + + pmask = gr->ppc_tpc_mask[gpc][ppc]; + while (ppc_tpcs-- > abits) + pmask &= pmask - 1; + amask |= (u64)pmask << (gpc * 8); + + pmask ^= gr->ppc_tpc_mask[gpc][ppc]; + bmask |= (u64)pmask << (gpc * 8); + + atarget -= min(abits, atarget); + btarget -= min(bbits, btarget); + if ((abits > 0) || (bbits > 0)) + alpha = !alpha; + } + } + + for (j = 0; j < gr->gpc_nr; j += 4, amask >>= 32, bmask >>= 32) { + nvkm_wr32(device, 0x406800 + (i * 0x20) + j, amask); + nvkm_wr32(device, 0x406c00 + (i * 0x20) + j, bmask); + } + } +} + +const struct gf100_grctx_func +gk104_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk104_grctx_pack_hub, + .gpc_0 = gk104_grctx_pack_gpc_0, + .gpc_1 = gk104_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gk104_grctx_pack_tpc, + .ppc = gk104_grctx_pack_ppc, + .icmd = gk104_grctx_pack_icmd, + .mthd = gk104_grctx_pack_mthd, + .bundle = gk104_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x600, + .pagepool = gk104_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf117_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .alpha_nr_max = 0x7ff, + .alpha_nr = 0x648, + .patch_ltc = gk104_grctx_generate_patch_ltc, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .alpha_beta_tables = gk104_grctx_generate_alpha_beta_tables, + .dist_skip_table = gf117_grctx_generate_dist_skip_table, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .r419f78 = gk104_grctx_generate_r419f78, + .r418800 = gk104_grctx_generate_r418800, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c new file mode 100644 index 000000000..e88740d4e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c @@ -0,0 +1,865 @@ +/* + * 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 "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gk110_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x0002f2, 2, 0x01, 0x00000001 }, + { 0x0002f5, 1, 0x01, 0x00000001 }, + { 0x0002f7, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000383, 1, 0x01, 0x00000011 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000662, 1, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x00080b, 1, 0x01, 0x00000002 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x000a0d, 1, 0x01, 0x00000006 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x003fffff }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00400008 }, + { 0x000841, 1, 0x01, 0x08000080 }, + { 0x000842, 1, 0x01, 0x00400008 }, + { 0x000843, 1, 0x01, 0x08000080 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000008 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +const struct gf100_gr_pack +gk110_grctx_pack_icmd[] = { + { gk110_grctx_init_icmd_0 }, + {} +}; + +static const struct gf100_gr_init +gk110_grctx_init_a197_0[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x00163c, 1, 0x04, 0x00000000 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x000dec, 1, 0x04, 0x00000001 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x0012ac, 1, 0x04, 0x00000001 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00000fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x000fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00260c, 1, 0x04, 0x00000000 }, + { 0x0007ac, 1, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x0002e4, 1, 0x04, 0x0000b001 }, + { 0x00036c, 2, 0x04, 0x00000000 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 4, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x0007a4, 2, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x000ddc, 1, 0x04, 0x00000002 }, + { 0x000374, 1, 0x04, 0x00000000 }, + { 0x000378, 1, 0x04, 0x00000020 }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x0017bc, 2, 0x04, 0x00000000 }, + { 0x0017c4, 1, 0x04, 0x00000001 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x00075c, 1, 0x04, 0x00000003 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x000208, 1, 0x04, 0x00000000 }, + { 0x0002cc, 2, 0x04, 0x003fffff }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00400008 }, + { 0x001284, 1, 0x04, 0x08000080 }, + { 0x001450, 1, 0x04, 0x00400008 }, + { 0x001454, 1, 0x04, 0x08000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gk110_grctx_pack_mthd[] = { + { gk110_grctx_init_a197_0, 0xa197 }, + { gf100_grctx_init_902d_0, 0x902d }, + {} +}; + +static const struct gf100_gr_init +gk110_grctx_init_fe_0[] = { + { 0x404004, 8, 0x04, 0x00000000 }, + { 0x404024, 1, 0x04, 0x0000e000 }, + { 0x404028, 8, 0x04, 0x00000000 }, + { 0x4040a8, 8, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf800008f }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404100, 10, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x40417c, 2, 0x04, 0x00000000 }, + { 0x4041a0, 4, 0x04, 0x00000000 }, + { 0x404200, 1, 0x04, 0x0000a197 }, + { 0x404204, 1, 0x04, 0x0000a1c0 }, + { 0x404208, 1, 0x04, 0x0000a140 }, + { 0x40420c, 1, 0x04, 0x0000902d }, + {} +}; + +const struct gf100_gr_init +gk110_grctx_init_pri_0[] = { + { 0x404404, 12, 0x04, 0x00000000 }, + { 0x404438, 1, 0x04, 0x00000000 }, + { 0x404460, 2, 0x04, 0x00000000 }, + { 0x404468, 1, 0x04, 0x00ffffff }, + { 0x40446c, 1, 0x04, 0x00000000 }, + { 0x404480, 1, 0x04, 0x00000001 }, + { 0x404498, 1, 0x04, 0x00000001 }, + {} +}; + +const struct gf100_gr_init +gk110_grctx_init_cwd_0[] = { + { 0x405b00, 1, 0x04, 0x00000000 }, + { 0x405b10, 1, 0x04, 0x00001000 }, + { 0x405b20, 1, 0x04, 0x04000000 }, + {} +}; + +static const struct gf100_gr_init +gk110_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x034103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b0, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x802000f0 }, + { 0x4064c4, 1, 0x04, 0x0192ffff }, + { 0x4064c8, 1, 0x04, 0x018007c0 }, + { 0x4064cc, 9, 0x04, 0x00000000 }, + { 0x4064fc, 1, 0x04, 0x0000022a }, + {} +}; + +static const struct gf100_gr_init +gk110_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x12802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1003e005 }, + { 0x408840, 1, 0x04, 0x0000000b }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x00c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +const struct gf100_gr_pack +gk110_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gk110_grctx_init_fe_0 }, + { gk110_grctx_init_pri_0 }, + { gk104_grctx_init_memfmt_0 }, + { gk104_grctx_init_ds_0 }, + { gk110_grctx_init_cwd_0 }, + { gk110_grctx_init_pd_0 }, + { gf100_grctx_init_rstr2d_0 }, + { gk104_grctx_init_scc_0 }, + { gk110_grctx_init_be_0 }, + {} +}; + +static const struct gf100_gr_init +gk110_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 1, 0x04, 0x00000000 }, + { 0x41880c, 1, 0x04, 0x00000030 }, + { 0x418810, 1, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00000044 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100018 }, + {} +}; + +const struct gf100_gr_init +gk110_grctx_init_gpc_unk_2[] = { + { 0x418d24, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gk110_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gf119_grctx_init_prop_0 }, + { gf119_grctx_init_gpc_unk_1 }, + { gk110_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +const struct gf100_gr_pack +gk110_grctx_pack_gpc_1[] = { + { gf119_grctx_init_crstr_0 }, + { gk104_grctx_init_gpm_0 }, + { gk110_grctx_init_gpc_unk_2 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +const struct gf100_gr_init +gk110_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000000f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000021 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x0000c000 }, + { 0x419a20, 1, 0x04, 0x00020800 }, + { 0x419a30, 1, 0x04, 0x00000001 }, + { 0x419ac4, 1, 0x04, 0x0037f440 }, + {} +}; + +const struct gf100_gr_init +gk110_grctx_init_mpc_0[] = { + { 0x419c00, 1, 0x04, 0x0000001a }, + { 0x419c04, 1, 0x04, 0x80000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + {} +}; + +const struct gf100_gr_init +gk110_grctx_init_l1c_0[] = { + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000203 }, + {} +}; + +static const struct gf100_gr_init +gk110_grctx_init_sm_0[] = { + { 0x419e04, 1, 0x04, 0x00000000 }, + { 0x419e08, 1, 0x04, 0x0000001d }, + { 0x419e0c, 1, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00001c02 }, + { 0x419e44, 1, 0x04, 0x0013eff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000007f }, + { 0x419e50, 2, 0x04, 0x00000000 }, + { 0x419e58, 1, 0x04, 0x00000001 }, + { 0x419e5c, 3, 0x04, 0x00000000 }, + { 0x419e68, 1, 0x04, 0x00000002 }, + { 0x419e6c, 12, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x00001f8f }, + { 0x419eb0, 1, 0x04, 0x0db00d2f }, + { 0x419eb8, 1, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0001304f }, + { 0x419f30, 4, 0x04, 0x00000000 }, + { 0x419f40, 1, 0x04, 0x00000018 }, + { 0x419f44, 3, 0x04, 0x00000000 }, + { 0x419f58, 1, 0x04, 0x00000000 }, + { 0x419f70, 1, 0x04, 0x00007300 }, + { 0x419f78, 1, 0x04, 0x000000eb }, + { 0x419f7c, 1, 0x04, 0x00000404 }, + {} +}; + +static const struct gf100_gr_pack +gk110_grctx_pack_tpc[] = { + { gf117_grctx_init_pe_0 }, + { gk110_grctx_init_tex_0 }, + { gk110_grctx_init_mpc_0 }, + { gk110_grctx_init_l1c_0 }, + { gk110_grctx_init_sm_0 }, + {} +}; + +static const struct gf100_gr_init +gk110_grctx_init_cbm_0[] = { + { 0x41bec0, 1, 0x04, 0x10000000 }, + { 0x41bec4, 1, 0x04, 0x00037f7f }, + { 0x41bee4, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_pack +gk110_grctx_pack_ppc[] = { + { gk104_grctx_init_pes_0 }, + { gk110_grctx_init_cbm_0 }, + { gf117_grctx_init_wwdx_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +void +gk110_grctx_generate_r419eb0(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419eb0, 0x00001000, 0x00001000); +} + +void +gk110_grctx_generate_r419f78(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + /* bit 3 set disables loads in fp helper invocations, we need it enabled */ + nvkm_mask(device, 0x419f78, 0x00000008, 0x00000000); +} + +const struct gf100_grctx_func +gk110_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk110_grctx_pack_hub, + .gpc_0 = gk110_grctx_pack_gpc_0, + .gpc_1 = gk110_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gk110_grctx_pack_tpc, + .ppc = gk110_grctx_pack_ppc, + .icmd = gk110_grctx_pack_icmd, + .mthd = gk110_grctx_pack_mthd, + .bundle = gk104_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x7c0, + .pagepool = gk104_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf117_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .alpha_nr_max = 0x7ff, + .alpha_nr = 0x648, + .patch_ltc = gk104_grctx_generate_patch_ltc, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .alpha_beta_tables = gk104_grctx_generate_alpha_beta_tables, + .dist_skip_table = gf117_grctx_generate_dist_skip_table, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .r418800 = gk104_grctx_generate_r418800, + .r419eb0 = gk110_grctx_generate_r419eb0, + .r419f78 = gk110_grctx_generate_r419f78, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c new file mode 100644 index 000000000..086e4d49e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c @@ -0,0 +1,105 @@ +/* + * 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 "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gk110b_grctx_init_sm_0[] = { + { 0x419e04, 1, 0x04, 0x00000000 }, + { 0x419e08, 1, 0x04, 0x0000001d }, + { 0x419e0c, 1, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00001c02 }, + { 0x419e44, 1, 0x04, 0x0013eff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000007f }, + { 0x419e50, 2, 0x04, 0x00000000 }, + { 0x419e58, 1, 0x04, 0x00000001 }, + { 0x419e5c, 3, 0x04, 0x00000000 }, + { 0x419e68, 1, 0x04, 0x00000002 }, + { 0x419e6c, 12, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x00001f8f }, + { 0x419eb0, 1, 0x04, 0x0db00d2f }, + { 0x419eb8, 1, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0001304f }, + { 0x419f30, 4, 0x04, 0x00000000 }, + { 0x419f40, 1, 0x04, 0x00000018 }, + { 0x419f44, 3, 0x04, 0x00000000 }, + { 0x419f58, 1, 0x04, 0x00000000 }, + { 0x419f70, 1, 0x04, 0x00006300 }, + { 0x419f78, 1, 0x04, 0x000000eb }, + { 0x419f7c, 1, 0x04, 0x00000404 }, + {} +}; + +static const struct gf100_gr_pack +gk110b_grctx_pack_tpc[] = { + { gf117_grctx_init_pe_0 }, + { gk110_grctx_init_tex_0 }, + { gk110_grctx_init_mpc_0 }, + { gk110_grctx_init_l1c_0 }, + { gk110b_grctx_init_sm_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +const struct gf100_grctx_func +gk110b_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk110_grctx_pack_hub, + .gpc_0 = gk110_grctx_pack_gpc_0, + .gpc_1 = gk110_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gk110b_grctx_pack_tpc, + .ppc = gk110_grctx_pack_ppc, + .icmd = gk110_grctx_pack_icmd, + .mthd = gk110_grctx_pack_mthd, + .bundle = gk104_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x600, + .pagepool = gk104_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf117_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .alpha_nr_max = 0x7ff, + .alpha_nr = 0x648, + .patch_ltc = gk104_grctx_generate_patch_ltc, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .alpha_beta_tables = gk104_grctx_generate_alpha_beta_tables, + .dist_skip_table = gf117_grctx_generate_dist_skip_table, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .r418800 = gk104_grctx_generate_r418800, + .r419eb0 = gk110_grctx_generate_r419eb0, + .r419f78 = gk110_grctx_generate_r419f78, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c new file mode 100644 index 000000000..0bf438c3f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c @@ -0,0 +1,570 @@ +/* + * 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 "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gk208_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x0002f2, 2, 0x01, 0x00000001 }, + { 0x0002f5, 1, 0x01, 0x00000001 }, + { 0x0002f7, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000383, 1, 0x01, 0x00000011 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000662, 1, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x00080b, 1, 0x01, 0x00000002 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x000a0d, 1, 0x01, 0x00000006 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x003fffff }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00400008 }, + { 0x000841, 1, 0x01, 0x08000080 }, + { 0x000842, 1, 0x01, 0x00400008 }, + { 0x000843, 1, 0x01, 0x08000080 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000008 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +gk208_grctx_pack_icmd[] = { + { gk208_grctx_init_icmd_0 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_fe_0[] = { + { 0x404004, 8, 0x04, 0x00000000 }, + { 0x404024, 1, 0x04, 0x0000e000 }, + { 0x404028, 8, 0x04, 0x00000000 }, + { 0x4040a8, 8, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf800008f }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404100, 10, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x40417c, 2, 0x04, 0x00000000 }, + { 0x404194, 1, 0x04, 0x01000700 }, + { 0x4041a0, 4, 0x04, 0x00000000 }, + { 0x404200, 1, 0x04, 0x0000a197 }, + { 0x404204, 1, 0x04, 0x0000a1c0 }, + { 0x404208, 1, 0x04, 0x0000a140 }, + { 0x40420c, 1, 0x04, 0x0000902d }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180648 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + { 0x405a1c, 1, 0x04, 0x000000ff }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x034103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b0, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x802000f0 }, + { 0x4064c4, 1, 0x04, 0x0192ffff }, + { 0x4064c8, 1, 0x04, 0x00c20200 }, + { 0x4064cc, 9, 0x04, 0x00000000 }, + { 0x4064fc, 1, 0x04, 0x0000022a }, + {} +}; + +const struct gf100_gr_init +gk208_grctx_init_rstr2d_0[] = { + { 0x407804, 1, 0x04, 0x00000063 }, + { 0x40780c, 1, 0x04, 0x0a418820 }, + { 0x407810, 1, 0x04, 0x062080e6 }, + { 0x407814, 1, 0x04, 0x020398a4 }, + { 0x407818, 1, 0x04, 0x0e629062 }, + { 0x40781c, 1, 0x04, 0x0a418820 }, + { 0x407820, 1, 0x04, 0x000000e6 }, + { 0x4078bc, 1, 0x04, 0x00000103 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x32802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1003e005 }, + { 0x408840, 1, 0x04, 0x0000000b }, + { 0x408900, 1, 0x04, 0xb080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x02c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +static const struct gf100_gr_pack +gk208_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gk208_grctx_init_fe_0 }, + { gk110_grctx_init_pri_0 }, + { gk104_grctx_init_memfmt_0 }, + { gk208_grctx_init_ds_0 }, + { gk110_grctx_init_cwd_0 }, + { gk208_grctx_init_pd_0 }, + { gk208_grctx_init_rstr2d_0 }, + { gk104_grctx_init_scc_0 }, + { gk208_grctx_init_be_0 }, + {} +}; + +const struct gf100_gr_init +gk208_grctx_init_prop_0[] = { + { 0x418400, 1, 0x04, 0x38005e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x02200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_gpc_unk_1[] = { + { 0x418600, 1, 0x04, 0x0000007f }, + { 0x418684, 1, 0x04, 0x0000001f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 2, 0x04, 0x00000080 }, + { 0x41870c, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x7006863a }, + { 0x418808, 1, 0x04, 0x00000000 }, + { 0x41880c, 1, 0x04, 0x00000030 }, + { 0x418810, 1, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00000044 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100058 }, + {} +}; + +const struct gf100_gr_init +gk208_grctx_init_crstr_0[] = { + { 0x418b00, 1, 0x04, 0x0000001e }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_gpm_0[] = { + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c40, 1, 0x04, 0xffffffff }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x2020000c }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +gk208_grctx_pack_gpc_0[] = { + { gf100_grctx_init_gpc_unk_0 }, + { gk208_grctx_init_prop_0 }, + { gk208_grctx_init_gpc_unk_1 }, + { gk208_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +static const struct gf100_gr_pack +gk208_grctx_pack_gpc_1[] = { + { gk208_grctx_init_crstr_0 }, + { gk208_grctx_init_gpm_0 }, + { gk110_grctx_init_gpc_unk_2 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000100f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000421 }, + { 0x419a0c, 1, 0x04, 0x00120000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x0000c000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419a30, 1, 0x04, 0x00000001 }, + { 0x419ac4, 1, 0x04, 0x0037f440 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_sm_0[] = { + { 0x419e04, 1, 0x04, 0x00000000 }, + { 0x419e08, 1, 0x04, 0x0000001d }, + { 0x419e0c, 1, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00001c02 }, + { 0x419e44, 1, 0x04, 0x0013eff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000007f }, + { 0x419e50, 2, 0x04, 0x00000000 }, + { 0x419e58, 1, 0x04, 0x00000001 }, + { 0x419e5c, 3, 0x04, 0x00000000 }, + { 0x419e68, 1, 0x04, 0x00000002 }, + { 0x419e6c, 12, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x00001f8f }, + { 0x419eb0, 1, 0x04, 0x0db00d2f }, + { 0x419eb8, 1, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0001304f }, + { 0x419f30, 4, 0x04, 0x00000000 }, + { 0x419f40, 1, 0x04, 0x00000018 }, + { 0x419f44, 3, 0x04, 0x00000000 }, + { 0x419f58, 1, 0x04, 0x00000020 }, + { 0x419f70, 1, 0x04, 0x00000000 }, + { 0x419f78, 1, 0x04, 0x000001eb }, + { 0x419f7c, 1, 0x04, 0x00000404 }, + {} +}; + +static const struct gf100_gr_pack +gk208_grctx_pack_tpc[] = { + { gf117_grctx_init_pe_0 }, + { gk208_grctx_init_tex_0 }, + { gk110_grctx_init_mpc_0 }, + { gk110_grctx_init_l1c_0 }, + { gk208_grctx_init_sm_0 }, + {} +}; + +static const struct gf100_gr_init +gk208_grctx_init_cbm_0[] = { + { 0x41bec0, 1, 0x04, 0x10000000 }, + { 0x41bec4, 1, 0x04, 0x00037f7f }, + { 0x41bee4, 1, 0x04, 0x00000000 }, + { 0x41bef0, 1, 0x04, 0x000003ff }, + {} +}; + +static const struct gf100_gr_pack +gk208_grctx_pack_ppc[] = { + { gk104_grctx_init_pes_0 }, + { gk208_grctx_init_cbm_0 }, + { gf117_grctx_init_wwdx_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +const struct gf100_grctx_func +gk208_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gk208_grctx_pack_hub, + .gpc_0 = gk208_grctx_pack_gpc_0, + .gpc_1 = gk208_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gk208_grctx_pack_tpc, + .ppc = gk208_grctx_pack_ppc, + .icmd = gk208_grctx_pack_icmd, + .mthd = gk110_grctx_pack_mthd, + .bundle = gk104_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0xc2, + .bundle_token_limit = 0x200, + .pagepool = gk104_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf117_grctx_generate_attrib, + .attrib_nr_max = 0x324, + .attrib_nr = 0x218, + .alpha_nr_max = 0x7ff, + .alpha_nr = 0x648, + .patch_ltc = gk104_grctx_generate_patch_ltc, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .alpha_beta_tables = gk104_grctx_generate_alpha_beta_tables, + .dist_skip_table = gf117_grctx_generate_dist_skip_table, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .r418800 = gk104_grctx_generate_r418800, + .r419f78 = gk110_grctx_generate_r419f78, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c new file mode 100644 index 000000000..c0d36bc60 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "ctxgf100.h" +#include "gf100.h" + +#include + +static void +gk20a_grctx_generate_main(struct gf100_gr *gr, struct gf100_grctx *info) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const struct gf100_grctx_func *grctx = gr->func->grctx; + u32 idle_timeout; + int i; + + gf100_gr_mmio(gr, gr->sw_ctx); + + gf100_gr_wait_idle(gr); + + idle_timeout = nvkm_mask(device, 0x404154, 0xffffffff, 0x00000000); + + grctx->attrib(info); + + grctx->unkn(gr); + + gf100_grctx_generate_floorsweep(gr); + + for (i = 0; i < 8; i++) + nvkm_wr32(device, 0x4064d0 + (i * 0x04), 0x00000000); + + nvkm_wr32(device, 0x405b00, (gr->tpc_total << 8) | gr->gpc_nr); + + nvkm_mask(device, 0x5044b0, 0x08000000, 0x08000000); + + gf100_gr_wait_idle(gr); + + nvkm_wr32(device, 0x404154, idle_timeout); + gf100_gr_wait_idle(gr); + + gf100_gr_mthd(gr, gr->method); + gf100_gr_wait_idle(gr); + + gf100_gr_icmd(gr, gr->bundle); + grctx->pagepool(info); + grctx->bundle(info); +} + +const struct gf100_grctx_func +gk20a_grctx = { + .main = gk20a_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .bundle = gk104_grctx_generate_bundle, + .bundle_size = 0x1800, + .bundle_min_gpm_fifo_depth = 0x62, + .bundle_token_limit = 0x100, + .pagepool = gk104_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gf117_grctx_generate_attrib, + .attrib_nr_max = 0x240, + .attrib_nr = 0x240, + .alpha_nr_max = 0x648 + (0x648 / 2), + .alpha_nr = 0x648, + .sm_id = gf100_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .alpha_beta_tables = gk104_grctx_generate_alpha_beta_tables, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c new file mode 100644 index 000000000..acdf0932a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c @@ -0,0 +1,995 @@ +/* + * 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 "ctxgf100.h" + +#include +#include + +/******************************************************************************* + * PGRAPH context register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gm107_grctx_init_icmd_0[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x0000b1, 2, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000a8, 1, 0x01, 0x0000ffff }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002f2, 2, 0x01, 0x00000001 }, + { 0x0002f5, 1, 0x01, 0x00000001 }, + { 0x0002f7, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x0000de, 1, 0x01, 0x00000001 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000383, 1, 0x01, 0x00000011 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x0005d0, 1, 0x01, 0x20181008 }, + { 0x0005d1, 1, 0x01, 0x40383028 }, + { 0x0005d2, 1, 0x01, 0x60585048 }, + { 0x0005d3, 1, 0x01, 0x80787068 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000550, 32, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x000595, 1, 0x01, 0x00400040 }, + { 0x000596, 1, 0x01, 0x00000492 }, + { 0x000597, 1, 0x01, 0x08080203 }, + { 0x0005ad, 1, 0x01, 0x00000008 }, + { 0x000598, 1, 0x01, 0x00020001 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 2, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000662, 1, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x000a0d, 1, 0x01, 0x00000006 }, + { 0x00097d, 1, 0x01, 0x0000000c }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000687, 1, 0x01, 0x003fffff }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00400008 }, + { 0x000841, 1, 0x01, 0x08000080 }, + { 0x000842, 1, 0x01, 0x00400008 }, + { 0x000843, 1, 0x01, 0x08000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 3, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 3, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000008 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +gm107_grctx_pack_icmd[] = { + { gm107_grctx_init_icmd_0 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_b097_0[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x001480, 8, 0x10, 0x00000000 }, + { 0x001484, 8, 0x10, 0x00000000 }, + { 0x001488, 8, 0x10, 0x00000000 }, + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000754, 1, 0x04, 0x00000001 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001080, 2, 0x04, 0x00000000 }, + { 0x001088, 2, 0x04, 0x00000001 }, + { 0x001090, 1, 0x04, 0x00000000 }, + { 0x001094, 1, 0x04, 0x00000001 }, + { 0x001098, 1, 0x04, 0x00000000 }, + { 0x00109c, 1, 0x04, 0x00000001 }, + { 0x0010a0, 2, 0x04, 0x00000000 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x000f40, 5, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00000fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x000fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00260c, 1, 0x04, 0x00000000 }, + { 0x0007ac, 1, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x0002e4, 1, 0x04, 0x0000b001 }, + { 0x00036c, 2, 0x04, 0x00000000 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000f60, 1, 0x04, 0x00000000 }, + { 0x000f64, 1, 0x04, 0x00400040 }, + { 0x000f68, 1, 0x04, 0x00002212 }, + { 0x000f6c, 1, 0x04, 0x08080203 }, + { 0x001108, 1, 0x04, 0x00000008 }, + { 0x000f70, 1, 0x04, 0x00080001 }, + { 0x000ffc, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 4, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x000dd0, 1, 0x04, 0x00000000 }, + { 0x000dd4, 1, 0x04, 0x00000001 }, + { 0x0002f4, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x0007a4, 2, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x000f14, 1, 0x04, 0x00000000 }, + { 0x000374, 1, 0x04, 0x00000000 }, + { 0x000378, 1, 0x04, 0x0000000c }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000da8, 1, 0x04, 0x00000030 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x0002d0, 1, 0x04, 0x003fffff }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00400008 }, + { 0x001284, 1, 0x04, 0x08000080 }, + { 0x001450, 1, 0x04, 0x00400008 }, + { 0x001454, 1, 0x04, 0x08000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gm107_grctx_pack_mthd[] = { + { gm107_grctx_init_b097_0, 0xb097 }, + { gf100_grctx_init_902d_0, 0x902d }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_fe_0[] = { + { 0x404004, 8, 0x04, 0x00000000 }, + { 0x404024, 1, 0x04, 0x0000e000 }, + { 0x404028, 8, 0x04, 0x00000000 }, + { 0x4040a8, 8, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf800008f }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404100, 10, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000045 }, + { 0x40417c, 2, 0x04, 0x00000000 }, + { 0x404194, 1, 0x04, 0x01000700 }, + { 0x4041a0, 4, 0x04, 0x00000000 }, + { 0x404200, 4, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_ds_0[] = { + { 0x405800, 1, 0x04, 0x0f8001bf }, + { 0x405830, 1, 0x04, 0x0aa01000 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + { 0x405a1c, 1, 0x04, 0x000000ff }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_pd_0[] = { + { 0x406020, 1, 0x04, 0x07410001 }, + { 0x406028, 4, 0x04, 0x00000001 }, + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b0, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x80400280 }, + { 0x4064c4, 1, 0x04, 0x0400ffff }, + { 0x4064c8, 1, 0x04, 0x018001ff }, + { 0x4064cc, 9, 0x04, 0x00000000 }, + { 0x4064fc, 1, 0x04, 0x0000022a }, + { 0x406500, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_be_0[] = { + { 0x408800, 1, 0x04, 0x32802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1003e005 }, + { 0x408840, 1, 0x04, 0x0000000b }, + { 0x408900, 1, 0x04, 0xb080b801 }, + { 0x408904, 1, 0x04, 0x63038001 }, + { 0x408908, 1, 0x04, 0x02c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +static const struct gf100_gr_pack +gm107_grctx_pack_hub[] = { + { gf100_grctx_init_main_0 }, + { gm107_grctx_init_fe_0 }, + { gk110_grctx_init_pri_0 }, + { gk104_grctx_init_memfmt_0 }, + { gm107_grctx_init_ds_0 }, + { gk110_grctx_init_cwd_0 }, + { gm107_grctx_init_pd_0 }, + { gk208_grctx_init_rstr2d_0 }, + { gk104_grctx_init_scc_0 }, + { gm107_grctx_init_be_0 }, + {} +}; + +const struct gf100_gr_init +gm107_grctx_init_gpc_unk_0[] = { + { 0x418380, 1, 0x04, 0x00000056 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_gpc_unk_1[] = { + { 0x418600, 1, 0x04, 0x0000007f }, + { 0x418684, 1, 0x04, 0x0000001f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 1, 0x04, 0x40000000 }, + { 0x41870c, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_setup_0[] = { + { 0x418800, 1, 0x04, 0x7006863a }, + { 0x418810, 1, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00000044 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100058 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_gpc_unk_2[] = { + { 0x418d24, 1, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x90000000 }, + { 0x418e24, 1, 0x04, 0x00000000 }, + { 0x418e28, 1, 0x04, 0x00000030 }, + { 0x418e30, 1, 0x04, 0x00000000 }, + { 0x418e34, 1, 0x04, 0x00010000 }, + { 0x418e38, 1, 0x04, 0x00000000 }, + { 0x418e40, 22, 0x04, 0x00000000 }, + { 0x418ea0, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gm107_grctx_pack_gpc_0[] = { + { gm107_grctx_init_gpc_unk_0 }, + { gk208_grctx_init_prop_0 }, + { gm107_grctx_init_gpc_unk_1 }, + { gm107_grctx_init_setup_0 }, + { gf100_grctx_init_zcull_0 }, + {} +}; + +static const struct gf100_gr_pack +gm107_grctx_pack_gpc_1[] = { + { gk208_grctx_init_crstr_0 }, + { gk104_grctx_init_gpm_0 }, + { gm107_grctx_init_gpc_unk_2 }, + { gf100_grctx_init_gcc_0 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_tex_0[] = { + { 0x419a00, 1, 0x04, 0x000300f0 }, + { 0x419a04, 1, 0x04, 0x00000005 }, + { 0x419a08, 1, 0x04, 0x00000421 }, + { 0x419a0c, 1, 0x04, 0x00120000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00002200 }, + { 0x419a1c, 1, 0x04, 0x0000c000 }, + { 0x419a20, 1, 0x04, 0x20008a00 }, + { 0x419a30, 1, 0x04, 0x00000001 }, + { 0x419a3c, 1, 0x04, 0x00000002 }, + { 0x419ac4, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_mpc_0[] = { + { 0x419c00, 1, 0x04, 0x0000001a }, + { 0x419c04, 1, 0x04, 0x80000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + { 0x419c2c, 1, 0x04, 0x00000000 }, + { 0x419c34, 1, 0x04, 0x01ff1ff3 }, + { 0x419c3c, 1, 0x04, 0x00001919 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_l1c_0[] = { + { 0x419c84, 1, 0x04, 0x00000020 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_sm_0[] = { + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00001c02 }, + { 0x419e44, 1, 0x04, 0x00d3eff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000007f }, + { 0x419e50, 1, 0x04, 0x00000000 }, + { 0x419e60, 4, 0x04, 0x00000000 }, + { 0x419e74, 10, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x0001cf8b }, + { 0x419eb0, 1, 0x04, 0x00030300 }, + { 0x419eb8, 1, 0x04, 0x00000000 }, + { 0x419ef0, 24, 0x04, 0x00000000 }, + { 0x419f68, 2, 0x04, 0x00000000 }, + { 0x419f70, 1, 0x04, 0x00000020 }, + { 0x419f78, 1, 0x04, 0x000003eb }, + { 0x419f7c, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gm107_grctx_pack_tpc[] = { + { gf117_grctx_init_pe_0 }, + { gm107_grctx_init_tex_0 }, + { gm107_grctx_init_mpc_0 }, + { gm107_grctx_init_l1c_0 }, + { gm107_grctx_init_sm_0 }, + {} +}; + +static const struct gf100_gr_init +gm107_grctx_init_cbm_0[] = { + { 0x41bec0, 1, 0x04, 0x00000000 }, + { 0x41bec4, 1, 0x04, 0x01050000 }, + { 0x41bee4, 1, 0x04, 0x00000000 }, + { 0x41bef0, 1, 0x04, 0x000003ff }, + { 0x41bef4, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gm107_grctx_init_wwdx_0[] = { + { 0x41bf00, 1, 0x04, 0x0a418820 }, + { 0x41bf04, 1, 0x04, 0x062080e6 }, + { 0x41bf08, 1, 0x04, 0x020398a4 }, + { 0x41bf0c, 1, 0x04, 0x0e629062 }, + { 0x41bf10, 1, 0x04, 0x0a418820 }, + { 0x41bf14, 1, 0x04, 0x000000e6 }, + { 0x41bfd0, 1, 0x04, 0x00900103 }, + { 0x41bfe0, 1, 0x04, 0x80000000 }, + { 0x41bfe4, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gm107_grctx_pack_ppc[] = { + { gk104_grctx_init_pes_0 }, + { gm107_grctx_init_cbm_0 }, + { gm107_grctx_init_wwdx_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +static void +gm107_grctx_generate_r419e00(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419e00, 0x00808080, 0x00808080); + nvkm_mask(device, 0x419ccc, 0x80000000, 0x80000000); + nvkm_mask(device, 0x419f80, 0x80000000, 0x80000000); + nvkm_mask(device, 0x419f88, 0x80000000, 0x80000000); +} + +void +gm107_grctx_generate_bundle(struct gf100_grctx *info) +{ + const struct gf100_grctx_func *grctx = info->gr->func->grctx; + const u32 state_limit = min(grctx->bundle_min_gpm_fifo_depth, + grctx->bundle_size / 0x20); + const u32 token_limit = grctx->bundle_token_limit; + const int s = 8; + const int b = mmio_vram(info, grctx->bundle_size, (1 << s), true); + mmio_refn(info, 0x408004, 0x00000000, s, b); + mmio_wr32(info, 0x408008, 0x80000000 | (grctx->bundle_size >> s)); + mmio_refn(info, 0x418e24, 0x00000000, s, b); + mmio_wr32(info, 0x418e28, 0x80000000 | (grctx->bundle_size >> s)); + mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit); +} + +void +gm107_grctx_generate_pagepool(struct gf100_grctx *info) +{ + const struct gf100_grctx_func *grctx = info->gr->func->grctx; + const int s = 8; + const int b = mmio_vram(info, grctx->pagepool_size, (1 << s), true); + mmio_refn(info, 0x40800c, 0x00000000, s, b); + mmio_wr32(info, 0x408010, 0x80000000); + mmio_refn(info, 0x419004, 0x00000000, s, b); + mmio_wr32(info, 0x419008, 0x00000000); + mmio_wr32(info, 0x4064cc, 0x80000000); + mmio_wr32(info, 0x418e30, 0x80000000); /* guess at it being related */ +} + +void +gm107_grctx_generate_attrib(struct gf100_grctx *info) +{ + struct gf100_gr *gr = info->gr; + const struct gf100_grctx_func *grctx = gr->func->grctx; + const u32 alpha = grctx->alpha_nr; + const u32 attrib = grctx->attrib_nr; + const u32 size = 0x20 * (grctx->attrib_nr_max + grctx->alpha_nr_max); + const int s = 12; + const int b = mmio_vram(info, size * gr->tpc_total, (1 << s), false); + const int max_batches = 0xffff; + u32 bo = 0; + u32 ao = bo + grctx->attrib_nr_max * gr->tpc_total; + int gpc, ppc, n = 0; + + mmio_refn(info, 0x418810, 0x80000000, s, b); + mmio_refn(info, 0x419848, 0x10000000, s, b); + mmio_refn(info, 0x419c2c, 0x10000000, s, b); + mmio_wr32(info, 0x405830, (attrib << 16) | alpha); + mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++, n++) { + const u32 as = alpha * gr->ppc_tpc_nr[gpc][ppc]; + const u32 bs = attrib * gr->ppc_tpc_nr[gpc][ppc]; + const u32 u = 0x418ea0 + (n * 0x04); + const u32 o = PPC_UNIT(gpc, ppc, 0); + if (!(gr->ppc_mask[gpc] & (1 << ppc))) + continue; + mmio_wr32(info, o + 0xc0, bs); + mmio_wr32(info, o + 0xf4, bo); + bo += grctx->attrib_nr_max * gr->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, o + 0xe4, as); + mmio_wr32(info, o + 0xf8, ao); + ao += grctx->alpha_nr_max * gr->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, u, ((bs / 3) << 16) | bs); + } + } +} + +static void +gm107_grctx_generate_r406500(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x406500, 0x00000001); +} + +void +gm107_grctx_generate_sm_id(struct gf100_gr *gr, int gpc, int tpc, int sm) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x698), sm); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0c10 + tpc * 4), sm); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x088), sm); +} + +const struct gf100_grctx_func +gm107_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .hub = gm107_grctx_pack_hub, + .gpc_0 = gm107_grctx_pack_gpc_0, + .gpc_1 = gm107_grctx_pack_gpc_1, + .zcull = gf100_grctx_pack_zcull, + .tpc = gm107_grctx_pack_tpc, + .ppc = gm107_grctx_pack_ppc, + .icmd = gm107_grctx_pack_icmd, + .mthd = gm107_grctx_pack_mthd, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x2c0, + .pagepool = gm107_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gm107_grctx_generate_attrib, + .attrib_nr_max = 0xff0, + .attrib_nr = 0xaa0, + .alpha_nr_max = 0x1800, + .alpha_nr = 0x1000, + .sm_id = gm107_grctx_generate_sm_id, + .tpc_nr = gf100_grctx_generate_tpc_nr, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .alpha_beta_tables = gk104_grctx_generate_alpha_beta_tables, + .dist_skip_table = gf117_grctx_generate_dist_skip_table, + .r406500 = gm107_grctx_generate_r406500, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .r419e00 = gm107_grctx_generate_r419e00, + .r419f78 = gk110_grctx_generate_r419f78, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm200.c new file mode 100644 index 000000000..013d05a0f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm200.c @@ -0,0 +1,128 @@ +/* + * Copyright 2015 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 "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +void +gm200_grctx_generate_r419a3c(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419a3c, 0x00000014, 0x00000000); +} + +static void +gm200_grctx_generate_r418e94(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x418e94, 0xffffffff, 0xc4230000); + nvkm_mask(device, 0x418e4c, 0xffffffff, 0x70000000); +} + +void +gm200_grctx_generate_smid_config(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 dist_nr = DIV_ROUND_UP(gr->tpc_total, 4); + u32 dist[TPC_MAX / 4] = {}; + u32 gpcs[GPC_MAX] = {}; + u8 sm, i; + + for (sm = 0; sm < gr->sm_nr; sm++) { + const u8 gpc = gr->sm[sm].gpc; + const u8 tpc = gr->sm[sm].tpc; + dist[sm / 4] |= ((gpc << 4) | tpc) << ((sm % 4) * 8); + gpcs[gpc] |= sm << (tpc * 8); + } + + for (i = 0; i < dist_nr; i++) + nvkm_wr32(device, 0x405b60 + (i * 4), dist[i]); + for (i = 0; i < gr->gpc_nr; i++) + nvkm_wr32(device, 0x405ba0 + (i * 4), gpcs[i]); +} + +void +gm200_grctx_generate_tpc_mask(struct gf100_gr *gr) +{ + u32 tmp, i; + for (tmp = 0, i = 0; i < gr->gpc_nr; i++) + tmp |= ((1 << gr->tpc_nr[i]) - 1) << (i * gr->func->tpc_nr); + nvkm_wr32(gr->base.engine.subdev.device, 0x4041c4, tmp); +} + +void +gm200_grctx_generate_r406500(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x406500, 0x00000000); +} + +void +gm200_grctx_generate_dist_skip_table(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 data[8] = {}; + int gpc, ppc, i; + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++) { + u8 ppc_tpcs = gr->ppc_tpc_nr[gpc][ppc]; + u8 ppc_tpcm = gr->ppc_tpc_mask[gpc][ppc]; + while (ppc_tpcs-- > gr->ppc_tpc_min) + ppc_tpcm &= ppc_tpcm - 1; + ppc_tpcm ^= gr->ppc_tpc_mask[gpc][ppc]; + ((u8 *)data)[gpc] |= ppc_tpcm; + } + } + + for (i = 0; i < ARRAY_SIZE(data); i++) + nvkm_wr32(device, 0x4064d0 + (i * 0x04), data[i]); +} + +const struct gf100_grctx_func +gm200_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x780, + .pagepool = gm107_grctx_generate_pagepool, + .pagepool_size = 0x20000, + .attrib = gm107_grctx_generate_attrib, + .attrib_nr_max = 0x600, + .attrib_nr = 0x400, + .alpha_nr_max = 0x1800, + .alpha_nr = 0x1000, + .sm_id = gm107_grctx_generate_sm_id, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .dist_skip_table = gm200_grctx_generate_dist_skip_table, + .r406500 = gm200_grctx_generate_r406500, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .tpc_mask = gm200_grctx_generate_tpc_mask, + .smid_config = gm200_grctx_generate_smid_config, + .r418e94 = gm200_grctx_generate_r418e94, + .r419a3c = gm200_grctx_generate_r419a3c, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm20b.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm20b.c new file mode 100644 index 000000000..6b92f8aa1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm20b.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "ctxgf100.h" + +static void +gm20b_grctx_generate_main(struct gf100_gr *gr, struct gf100_grctx *info) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const struct gf100_grctx_func *grctx = gr->func->grctx; + u32 idle_timeout; + int i, tmp; + + gf100_gr_mmio(gr, gr->sw_ctx); + + gf100_gr_wait_idle(gr); + + idle_timeout = nvkm_mask(device, 0x404154, 0xffffffff, 0x00000000); + + grctx->attrib(info); + + grctx->unkn(gr); + + gf100_grctx_generate_floorsweep(gr); + + for (i = 0; i < 8; i++) + nvkm_wr32(device, 0x4064d0 + (i * 0x04), 0x00000000); + + nvkm_wr32(device, 0x405b00, (gr->tpc_total << 8) | gr->gpc_nr); + + nvkm_wr32(device, 0x408908, nvkm_rd32(device, 0x410108) | 0x80000000); + + for (tmp = 0, i = 0; i < gr->gpc_nr; i++) + tmp |= ((1 << gr->tpc_nr[i]) - 1) << (i * 4); + nvkm_wr32(device, 0x4041c4, tmp); + + gm200_grctx_generate_smid_config(gr); + + gf100_gr_wait_idle(gr); + + nvkm_wr32(device, 0x404154, idle_timeout); + gf100_gr_wait_idle(gr); + + gf100_gr_mthd(gr, gr->method); + gf100_gr_wait_idle(gr); + + gf100_gr_icmd(gr, gr->bundle); + grctx->pagepool(info); + grctx->bundle(info); +} + +const struct gf100_grctx_func +gm20b_grctx = { + .main = gm20b_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x1800, + .bundle_min_gpm_fifo_depth = 0x182, + .bundle_token_limit = 0x1c0, + .pagepool = gm107_grctx_generate_pagepool, + .pagepool_size = 0x8000, + .attrib = gm107_grctx_generate_attrib, + .attrib_nr_max = 0x600, + .attrib_nr = 0x400, + .alpha_nr_max = 0xc00, + .alpha_nr = 0x800, + .sm_id = gm107_grctx_generate_sm_id, + .rop_mapping = gf117_grctx_generate_rop_mapping, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c new file mode 100644 index 000000000..0b3326262 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c @@ -0,0 +1,139 @@ +/* + * Copyright 2016 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 "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +void +gp100_grctx_generate_pagepool(struct gf100_grctx *info) +{ + const struct gf100_grctx_func *grctx = info->gr->func->grctx; + const int s = 8; + const int b = mmio_vram(info, grctx->pagepool_size, (1 << s), true); + mmio_refn(info, 0x40800c, 0x00000000, s, b); + mmio_wr32(info, 0x408010, 0x8007d800); + mmio_refn(info, 0x419004, 0x00000000, s, b); + mmio_wr32(info, 0x419008, 0x00000000); +} + +static void +gp100_grctx_generate_attrib(struct gf100_grctx *info) +{ + struct gf100_gr *gr = info->gr; + const struct gf100_grctx_func *grctx = gr->func->grctx; + const u32 alpha = grctx->alpha_nr; + const u32 attrib = grctx->attrib_nr; + const int s = 12; + const int max_batches = 0xffff; + u32 size = grctx->alpha_nr_max * gr->tpc_total; + u32 ao = 0; + u32 bo = ao + size; + int gpc, ppc, b, n = 0; + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) + size += grctx->attrib_nr_max * gr->ppc_nr[gpc] * gr->ppc_tpc_max; + size = ((size * 0x20) + 128) & ~127; + b = mmio_vram(info, size, (1 << s), false); + + mmio_refn(info, 0x418810, 0x80000000, s, b); + mmio_refn(info, 0x419848, 0x10000000, s, b); + mmio_refn(info, 0x419c2c, 0x10000000, s, b); + mmio_refn(info, 0x419b00, 0x00000000, s, b); + mmio_wr32(info, 0x419b04, 0x80000000 | size >> 7); + mmio_wr32(info, 0x405830, attrib); + mmio_wr32(info, 0x40585c, alpha); + mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++, n++) { + const u32 as = alpha * gr->ppc_tpc_nr[gpc][ppc]; + const u32 bs = attrib * gr->ppc_tpc_max; + const u32 u = 0x418ea0 + (n * 0x04); + const u32 o = PPC_UNIT(gpc, ppc, 0); + if (!(gr->ppc_mask[gpc] & (1 << ppc))) + continue; + mmio_wr32(info, o + 0xc0, bs); + mmio_wr32(info, o + 0xf4, bo); + mmio_wr32(info, o + 0xf0, bs); + bo += grctx->attrib_nr_max * gr->ppc_tpc_max; + mmio_wr32(info, o + 0xe4, as); + mmio_wr32(info, o + 0xf8, ao); + ao += grctx->alpha_nr_max * gr->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, u, bs); + } + } + + mmio_wr32(info, 0x418eec, 0x00000000); + mmio_wr32(info, 0x41befc, 0x00000000); +} + +void +gp100_grctx_generate_smid_config(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 dist_nr = DIV_ROUND_UP(gr->tpc_total, 4); + u32 dist[TPC_MAX / 4] = {}, gpcs[16] = {}; + u8 sm, i; + + for (sm = 0; sm < gr->sm_nr; sm++) { + const u8 gpc = gr->sm[sm].gpc; + const u8 tpc = gr->sm[sm].tpc; + dist[sm / 4] |= ((gpc << 4) | tpc) << ((sm % 4) * 8); + gpcs[gpc + (gr->func->gpc_nr * (tpc / 4))] |= sm << ((tpc % 4) * 8); + } + + for (i = 0; i < dist_nr; i++) + nvkm_wr32(device, 0x405b60 + (i * 4), dist[i]); + for (i = 0; i < ARRAY_SIZE(gpcs); i++) + nvkm_wr32(device, 0x405ba0 + (i * 4), gpcs[i]); +} + +const struct gf100_grctx_func +gp100_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x1080, + .pagepool = gp100_grctx_generate_pagepool, + .pagepool_size = 0x20000, + .attrib = gp100_grctx_generate_attrib, + .attrib_nr_max = 0x660, + .attrib_nr = 0x440, + .alpha_nr_max = 0xc00, + .alpha_nr = 0x800, + .sm_id = gm107_grctx_generate_sm_id, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .dist_skip_table = gm200_grctx_generate_dist_skip_table, + .r406500 = gm200_grctx_generate_r406500, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .tpc_mask = gm200_grctx_generate_tpc_mask, + .smid_config = gp100_grctx_generate_smid_config, + .r419a3c = gm200_grctx_generate_r419a3c, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c new file mode 100644 index 000000000..daee17bf7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c @@ -0,0 +1,119 @@ +/* + * Copyright 2016 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 "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +static void +gp102_grctx_generate_r408840(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x408840, 0x00000003, 0x00000000); +} + +void +gp102_grctx_generate_attrib(struct gf100_grctx *info) +{ + struct gf100_gr *gr = info->gr; + const struct gf100_grctx_func *grctx = gr->func->grctx; + const u32 alpha = grctx->alpha_nr; + const u32 attrib = grctx->attrib_nr; + const u32 gfxp = grctx->gfxp_nr; + const int s = 12; + const int max_batches = 0xffff; + u32 size = grctx->alpha_nr_max * gr->tpc_total; + u32 ao = 0; + u32 bo = ao + size; + int gpc, ppc, b, n = 0; + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) + size += grctx->gfxp_nr * gr->ppc_nr[gpc] * gr->ppc_tpc_max; + size = ((size * 0x20) + 128) & ~127; + b = mmio_vram(info, size, (1 << s), false); + + mmio_refn(info, 0x418810, 0x80000000, s, b); + mmio_refn(info, 0x419848, 0x10000000, s, b); + mmio_refn(info, 0x419c2c, 0x10000000, s, b); + mmio_refn(info, 0x419b00, 0x00000000, s, b); + mmio_wr32(info, 0x419b04, 0x80000000 | size >> 7); + mmio_wr32(info, 0x405830, attrib); + mmio_wr32(info, 0x40585c, alpha); + mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++, n++) { + const u32 as = alpha * gr->ppc_tpc_nr[gpc][ppc]; + const u32 bs = attrib * gr->ppc_tpc_max; + const u32 gs = gfxp * gr->ppc_tpc_max; + const u32 u = 0x418ea0 + (n * 0x04); + const u32 o = PPC_UNIT(gpc, ppc, 0); + const u32 p = GPC_UNIT(gpc, 0xc44 + (ppc * 4)); + if (!(gr->ppc_mask[gpc] & (1 << ppc))) + continue; + mmio_wr32(info, o + 0xc0, gs); + mmio_wr32(info, p, bs); + mmio_wr32(info, o + 0xf4, bo); + mmio_wr32(info, o + 0xf0, bs); + bo += gs; + mmio_wr32(info, o + 0xe4, as); + mmio_wr32(info, o + 0xf8, ao); + ao += grctx->alpha_nr_max * gr->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, u, bs); + } + } + + mmio_wr32(info, 0x4181e4, 0x00000100); + mmio_wr32(info, 0x41befc, 0x00000100); +} + +const struct gf100_grctx_func +gp102_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x900, + .pagepool = gp100_grctx_generate_pagepool, + .pagepool_size = 0x20000, + .attrib = gp102_grctx_generate_attrib, + .attrib_nr_max = 0x4b0, + .attrib_nr = 0x320, + .alpha_nr_max = 0xc00, + .alpha_nr = 0x800, + .gfxp_nr = 0xba8, + .sm_id = gm107_grctx_generate_sm_id, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .dist_skip_table = gm200_grctx_generate_dist_skip_table, + .r406500 = gm200_grctx_generate_r406500, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .tpc_mask = gm200_grctx_generate_tpc_mask, + .smid_config = gp100_grctx_generate_smid_config, + .r419a3c = gm200_grctx_generate_r419a3c, + .r408840 = gp102_grctx_generate_r408840, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp104.c new file mode 100644 index 000000000..3b85e3d32 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp104.c @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ctxgf100.h" + +const struct gf100_grctx_func +gp104_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x900, + .pagepool = gp100_grctx_generate_pagepool, + .pagepool_size = 0x20000, + .attrib = gp102_grctx_generate_attrib, + .attrib_nr_max = 0x4b0, + .attrib_nr = 0x320, + .alpha_nr_max = 0xc00, + .alpha_nr = 0x800, + .gfxp_nr = 0xba8, + .sm_id = gm107_grctx_generate_sm_id, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .dist_skip_table = gm200_grctx_generate_dist_skip_table, + .r406500 = gm200_grctx_generate_r406500, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .tpc_mask = gm200_grctx_generate_tpc_mask, + .smid_config = gp100_grctx_generate_smid_config, + .r419a3c = gm200_grctx_generate_r419a3c, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp107.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp107.c new file mode 100644 index 000000000..5060c5ee5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp107.c @@ -0,0 +1,56 @@ +/* + * Copyright 2017 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 "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +const struct gf100_grctx_func +gp107_grctx = { + .main = gf100_grctx_generate_main, + .unkn = gk104_grctx_generate_unkn, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x300, + .pagepool = gp100_grctx_generate_pagepool, + .pagepool_size = 0x20000, + .attrib = gp102_grctx_generate_attrib, + .attrib_nr_max = 0x15de, + .attrib_nr = 0x540, + .alpha_nr_max = 0xc00, + .alpha_nr = 0x800, + .gfxp_nr = 0xe94, + .sm_id = gm107_grctx_generate_sm_id, + .rop_mapping = gf117_grctx_generate_rop_mapping, + .dist_skip_table = gm200_grctx_generate_dist_skip_table, + .r406500 = gm200_grctx_generate_r406500, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .tpc_mask = gm200_grctx_generate_tpc_mask, + .smid_config = gp100_grctx_generate_smid_config, + .r419a3c = gm200_grctx_generate_r419a3c, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgv100.c new file mode 100644 index 000000000..39553d55d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgv100.c @@ -0,0 +1,214 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ctxgf100.h" + +/******************************************************************************* + * PGRAPH context implementation + ******************************************************************************/ + +const struct gf100_gr_init +gv100_grctx_init_sw_veid_bundle_init_0[] = { + { 0x00001000, 64, 0x00100000, 0x00000008 }, + { 0x00000941, 64, 0x00100000, 0x00000000 }, + { 0x0000097e, 64, 0x00100000, 0x00000000 }, + { 0x0000097f, 64, 0x00100000, 0x00000100 }, + { 0x0000035c, 64, 0x00100000, 0x00000000 }, + { 0x0000035d, 64, 0x00100000, 0x00000000 }, + { 0x00000a08, 64, 0x00100000, 0x00000000 }, + { 0x00000a09, 64, 0x00100000, 0x00000000 }, + { 0x00000a0a, 64, 0x00100000, 0x00000000 }, + { 0x00000352, 64, 0x00100000, 0x00000000 }, + { 0x00000353, 64, 0x00100000, 0x00000000 }, + { 0x00000358, 64, 0x00100000, 0x00000000 }, + { 0x00000359, 64, 0x00100000, 0x00000000 }, + { 0x00000370, 64, 0x00100000, 0x00000000 }, + { 0x00000371, 64, 0x00100000, 0x00000000 }, + { 0x00000372, 64, 0x00100000, 0x000fffff }, + { 0x00000366, 64, 0x00100000, 0x00000000 }, + { 0x00000367, 64, 0x00100000, 0x00000000 }, + { 0x00000368, 64, 0x00100000, 0x00000fff }, + { 0x00000623, 64, 0x00100000, 0x00000000 }, + { 0x00000624, 64, 0x00100000, 0x00000000 }, + { 0x0001e100, 1, 0x00000001, 0x02000001 }, + {} +}; + +static const struct gf100_gr_pack +gv100_grctx_pack_sw_veid_bundle_init[] = { + { gv100_grctx_init_sw_veid_bundle_init_0 }, + {} +}; + +void +gv100_grctx_generate_attrib(struct gf100_grctx *info) +{ + struct gf100_gr *gr = info->gr; + const struct gf100_grctx_func *grctx = gr->func->grctx; + const u32 alpha = grctx->alpha_nr; + const u32 attrib = grctx->attrib_nr; + const u32 gfxp = grctx->gfxp_nr; + const int s = 12; + u32 size = grctx->alpha_nr_max * gr->tpc_total; + u32 ao = 0; + u32 bo = ao + size; + int gpc, ppc, b, n = 0; + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) + size += grctx->gfxp_nr * gr->ppc_nr[gpc] * gr->ppc_tpc_max; + size = ((size * 0x20) + 127) & ~127; + b = mmio_vram(info, size, (1 << s), false); + + mmio_refn(info, 0x418810, 0x80000000, s, b); + mmio_refn(info, 0x419848, 0x10000000, s, b); + mmio_refn(info, 0x419c2c, 0x10000000, s, b); + mmio_refn(info, 0x419e00, 0x00000000, s, b); + mmio_wr32(info, 0x419e04, 0x80000000 | size >> 7); + mmio_wr32(info, 0x405830, attrib); + mmio_wr32(info, 0x40585c, alpha); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++, n++) { + const u32 as = alpha * gr->ppc_tpc_nr[gpc][ppc]; + const u32 bs = attrib * gr->ppc_tpc_max; + const u32 gs = gfxp * gr->ppc_tpc_max; + const u32 u = 0x418ea0 + (n * 0x04); + const u32 o = PPC_UNIT(gpc, ppc, 0); + if (!(gr->ppc_mask[gpc] & (1 << ppc))) + continue; + mmio_wr32(info, o + 0xc0, gs); + mmio_wr32(info, o + 0xf4, bo); + mmio_wr32(info, o + 0xf0, bs); + bo += gs; + mmio_wr32(info, o + 0xe4, as); + mmio_wr32(info, o + 0xf8, ao); + ao += grctx->alpha_nr_max * gr->ppc_tpc_nr[gpc][ppc]; + mmio_wr32(info, u, bs); + } + } + + mmio_wr32(info, 0x4181e4, 0x00000100); + mmio_wr32(info, 0x41befc, 0x00000100); +} + +void +gv100_grctx_generate_rop_mapping(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 data; + int i, j; + + /* Pack tile map into register format. */ + nvkm_wr32(device, 0x418bb8, (gr->tpc_total << 8) | + gr->screen_tile_row_offset); + for (i = 0; i < 11; i++) { + for (data = 0, j = 0; j < 6; j++) + data |= (gr->tile[i * 6 + j] & 0x1f) << (j * 5); + nvkm_wr32(device, 0x418b08 + (i * 4), data); + nvkm_wr32(device, 0x41bf00 + (i * 4), data); + nvkm_wr32(device, 0x40780c + (i * 4), data); + } + + /* GPC_BROADCAST.TP_BROADCAST */ + nvkm_wr32(device, 0x41bfd0, (gr->tpc_total << 8) | + gr->screen_tile_row_offset); + for (i = 0, j = 1; i < 5; i++, j += 4) { + u8 v19 = (1 << (j + 0)) % gr->tpc_total; + u8 v20 = (1 << (j + 1)) % gr->tpc_total; + u8 v21 = (1 << (j + 2)) % gr->tpc_total; + u8 v22 = (1 << (j + 3)) % gr->tpc_total; + nvkm_wr32(device, 0x41bfb0 + (i * 4), (v22 << 24) | + (v21 << 16) | + (v20 << 8) | + v19); + } + + /* UNK78xx */ + nvkm_wr32(device, 0x4078bc, (gr->tpc_total << 8) | + gr->screen_tile_row_offset); +} + +void +gv100_grctx_generate_r400088(struct gf100_gr *gr, bool on) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x400088, 0x00060000, on ? 0x00060000 : 0x00000000); +} + +static void +gv100_grctx_generate_sm_id(struct gf100_gr *gr, int gpc, int tpc, int sm) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x608), sm); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0c10 + tpc * 4), sm); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x088), sm); +} + +void +gv100_grctx_generate_unkn(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x41980c, 0x00000010, 0x00000010); + nvkm_mask(device, 0x41be08, 0x00000004, 0x00000004); + nvkm_mask(device, 0x4064c0, 0x80000000, 0x80000000); + nvkm_mask(device, 0x405800, 0x08000000, 0x08000000); + nvkm_mask(device, 0x419c00, 0x00000008, 0x00000008); +} + +void +gv100_grctx_unkn88c(struct gf100_gr *gr, bool on) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 mask = 0x00000010, data = on ? mask : 0x00000000; + nvkm_mask(device, 0x40988c, mask, data); + nvkm_rd32(device, 0x40988c); + nvkm_mask(device, 0x41a88c, mask, data); + nvkm_rd32(device, 0x41a88c); + nvkm_mask(device, 0x408a14, mask, data); + nvkm_rd32(device, 0x408a14); +} + +const struct gf100_grctx_func +gv100_grctx = { + .unkn88c = gv100_grctx_unkn88c, + .main = gf100_grctx_generate_main, + .unkn = gv100_grctx_generate_unkn, + .sw_veid_bundle_init = gv100_grctx_pack_sw_veid_bundle_init, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0x1680, + .pagepool = gp100_grctx_generate_pagepool, + .pagepool_size = 0x20000, + .attrib = gv100_grctx_generate_attrib, + .attrib_nr_max = 0x6c0, + .attrib_nr = 0x480, + .alpha_nr_max = 0xc00, + .alpha_nr = 0x800, + .gfxp_nr = 0xd10, + .sm_id = gv100_grctx_generate_sm_id, + .rop_mapping = gv100_grctx_generate_rop_mapping, + .dist_skip_table = gm200_grctx_generate_dist_skip_table, + .r406500 = gm200_grctx_generate_r406500, + .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, + .smid_config = gp100_grctx_generate_smid_config, + .r400088 = gv100_grctx_generate_r400088, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c new file mode 100644 index 000000000..80a6b017a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.c @@ -0,0 +1,693 @@ +/* + * Copyright 2009 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 + */ + +/* NVIDIA context programs handle a number of other conditions which are + * not implemented in our versions. It's not clear why NVIDIA context + * programs have this code, nor whether it's strictly necessary for + * correct operation. We'll implement additional handling if/when we + * discover it's necessary. + * + * - On context save, NVIDIA set 0x400314 bit 0 to 1 if the "3D state" + * flag is set, this gets saved into the context. + * - On context save, the context program for all cards load nsource + * into a flag register and check for ILLEGAL_MTHD. If it's set, + * opcode 0x60000d is called before resuming normal operation. + * - Some context programs check more conditions than the above. NV44 + * checks: ((nsource & 0x0857) || (0x400718 & 0x0100) || (intr & 0x0001)) + * and calls 0x60000d before resuming normal operation. + * - At the very beginning of NVIDIA's context programs, flag 9 is checked + * and if true 0x800001 is called with count=0, pos=0, the flag is cleared + * and then the ctxprog is aborted. It looks like a complicated NOP, + * its purpose is unknown. + * - In the section of code that loads the per-vs state, NVIDIA check + * flag 10. If it's set, they only transfer the small 0x300 byte block + * of state + the state for a single vs as opposed to the state for + * all vs units. It doesn't seem likely that it'll occur in normal + * operation, especially seeing as it appears NVIDIA may have screwed + * up the ctxprogs for some cards and have an invalid instruction + * rather than a cp_lsr(ctx, dwords_for_1_vs_unit) instruction. + * - There's a number of places where context offset 0 (where we place + * the PRAMIN offset of the context) is loaded into either 0x408000, + * 0x408004 or 0x408008. Not sure what's up there either. + * - The ctxprogs for some cards save 0x400a00 again during the cleanup + * path for auto-loadctx. + */ + +#define CP_FLAG_CLEAR 0 +#define CP_FLAG_SET 1 +#define CP_FLAG_SWAP_DIRECTION ((0 * 32) + 0) +#define CP_FLAG_SWAP_DIRECTION_LOAD 0 +#define CP_FLAG_SWAP_DIRECTION_SAVE 1 +#define CP_FLAG_USER_SAVE ((0 * 32) + 5) +#define CP_FLAG_USER_SAVE_NOT_PENDING 0 +#define CP_FLAG_USER_SAVE_PENDING 1 +#define CP_FLAG_USER_LOAD ((0 * 32) + 6) +#define CP_FLAG_USER_LOAD_NOT_PENDING 0 +#define CP_FLAG_USER_LOAD_PENDING 1 +#define CP_FLAG_STATUS ((3 * 32) + 0) +#define CP_FLAG_STATUS_IDLE 0 +#define CP_FLAG_STATUS_BUSY 1 +#define CP_FLAG_AUTO_SAVE ((3 * 32) + 4) +#define CP_FLAG_AUTO_SAVE_NOT_PENDING 0 +#define CP_FLAG_AUTO_SAVE_PENDING 1 +#define CP_FLAG_AUTO_LOAD ((3 * 32) + 5) +#define CP_FLAG_AUTO_LOAD_NOT_PENDING 0 +#define CP_FLAG_AUTO_LOAD_PENDING 1 +#define CP_FLAG_UNK54 ((3 * 32) + 6) +#define CP_FLAG_UNK54_CLEAR 0 +#define CP_FLAG_UNK54_SET 1 +#define CP_FLAG_ALWAYS ((3 * 32) + 8) +#define CP_FLAG_ALWAYS_FALSE 0 +#define CP_FLAG_ALWAYS_TRUE 1 +#define CP_FLAG_UNK57 ((3 * 32) + 9) +#define CP_FLAG_UNK57_CLEAR 0 +#define CP_FLAG_UNK57_SET 1 + +#define CP_CTX 0x00100000 +#define CP_CTX_COUNT 0x000fc000 +#define CP_CTX_COUNT_SHIFT 14 +#define CP_CTX_REG 0x00003fff +#define CP_LOAD_SR 0x00200000 +#define CP_LOAD_SR_VALUE 0x000fffff +#define CP_BRA 0x00400000 +#define CP_BRA_IP 0x0000ff00 +#define CP_BRA_IP_SHIFT 8 +#define CP_BRA_IF_CLEAR 0x00000080 +#define CP_BRA_FLAG 0x0000007f +#define CP_WAIT 0x00500000 +#define CP_WAIT_SET 0x00000080 +#define CP_WAIT_FLAG 0x0000007f +#define CP_SET 0x00700000 +#define CP_SET_1 0x00000080 +#define CP_SET_FLAG 0x0000007f +#define CP_NEXT_TO_SWAP 0x00600007 +#define CP_NEXT_TO_CURRENT 0x00600009 +#define CP_SET_CONTEXT_POINTER 0x0060000a +#define CP_END 0x0060000e +#define CP_LOAD_MAGIC_UNK01 0x00800001 /* unknown */ +#define CP_LOAD_MAGIC_NV44TCL 0x00800029 /* per-vs state (0x4497) */ +#define CP_LOAD_MAGIC_NV40TCL 0x00800041 /* per-vs state (0x4097) */ + +#include "ctxnv40.h" +#include "nv40.h" + +/* TODO: + * - get vs count from 0x1540 + */ + +static int +nv40_gr_vs_count(struct nvkm_device *device) +{ + + switch (device->chipset) { + case 0x47: + case 0x49: + case 0x4b: + return 8; + case 0x40: + return 6; + case 0x41: + case 0x42: + return 5; + case 0x43: + case 0x44: + case 0x46: + case 0x4a: + return 3; + case 0x4c: + case 0x4e: + case 0x67: + default: + return 1; + } +} + + +enum cp_label { + cp_check_load = 1, + cp_setup_auto_load, + cp_setup_load, + cp_setup_save, + cp_swap_state, + cp_swap_state3d_3_is_save, + cp_prepare_exit, + cp_exit, +}; + +static void +nv40_gr_construct_general(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i; + + cp_ctx(ctx, 0x4000a4, 1); + gr_def(ctx, 0x4000a4, 0x00000008); + cp_ctx(ctx, 0x400144, 58); + gr_def(ctx, 0x400144, 0x00000001); + cp_ctx(ctx, 0x400314, 1); + gr_def(ctx, 0x400314, 0x00000000); + cp_ctx(ctx, 0x400400, 10); + cp_ctx(ctx, 0x400480, 10); + cp_ctx(ctx, 0x400500, 19); + gr_def(ctx, 0x400514, 0x00040000); + gr_def(ctx, 0x400524, 0x55555555); + gr_def(ctx, 0x400528, 0x55555555); + gr_def(ctx, 0x40052c, 0x55555555); + gr_def(ctx, 0x400530, 0x55555555); + cp_ctx(ctx, 0x400560, 6); + gr_def(ctx, 0x400568, 0x0000ffff); + gr_def(ctx, 0x40056c, 0x0000ffff); + cp_ctx(ctx, 0x40057c, 5); + cp_ctx(ctx, 0x400710, 3); + gr_def(ctx, 0x400710, 0x20010001); + gr_def(ctx, 0x400714, 0x0f73ef00); + cp_ctx(ctx, 0x400724, 1); + gr_def(ctx, 0x400724, 0x02008821); + cp_ctx(ctx, 0x400770, 3); + if (device->chipset == 0x40) { + cp_ctx(ctx, 0x400814, 4); + cp_ctx(ctx, 0x400828, 5); + cp_ctx(ctx, 0x400840, 5); + gr_def(ctx, 0x400850, 0x00000040); + cp_ctx(ctx, 0x400858, 4); + gr_def(ctx, 0x400858, 0x00000040); + gr_def(ctx, 0x40085c, 0x00000040); + gr_def(ctx, 0x400864, 0x80000000); + cp_ctx(ctx, 0x40086c, 9); + gr_def(ctx, 0x40086c, 0x80000000); + gr_def(ctx, 0x400870, 0x80000000); + gr_def(ctx, 0x400874, 0x80000000); + gr_def(ctx, 0x400878, 0x80000000); + gr_def(ctx, 0x400888, 0x00000040); + gr_def(ctx, 0x40088c, 0x80000000); + cp_ctx(ctx, 0x4009c0, 8); + gr_def(ctx, 0x4009cc, 0x80000000); + gr_def(ctx, 0x4009dc, 0x80000000); + } else { + cp_ctx(ctx, 0x400840, 20); + if (nv44_gr_class(ctx->device)) { + for (i = 0; i < 8; i++) + gr_def(ctx, 0x400860 + (i * 4), 0x00000001); + } + gr_def(ctx, 0x400880, 0x00000040); + gr_def(ctx, 0x400884, 0x00000040); + gr_def(ctx, 0x400888, 0x00000040); + cp_ctx(ctx, 0x400894, 11); + gr_def(ctx, 0x400894, 0x00000040); + if (!nv44_gr_class(ctx->device)) { + for (i = 0; i < 8; i++) + gr_def(ctx, 0x4008a0 + (i * 4), 0x80000000); + } + cp_ctx(ctx, 0x4008e0, 2); + cp_ctx(ctx, 0x4008f8, 2); + if (device->chipset == 0x4c || + (device->chipset & 0xf0) == 0x60) + cp_ctx(ctx, 0x4009f8, 1); + } + cp_ctx(ctx, 0x400a00, 73); + gr_def(ctx, 0x400b0c, 0x0b0b0b0c); + cp_ctx(ctx, 0x401000, 4); + cp_ctx(ctx, 0x405004, 1); + switch (device->chipset) { + case 0x47: + case 0x49: + case 0x4b: + cp_ctx(ctx, 0x403448, 1); + gr_def(ctx, 0x403448, 0x00001010); + break; + default: + cp_ctx(ctx, 0x403440, 1); + switch (device->chipset) { + case 0x40: + gr_def(ctx, 0x403440, 0x00000010); + break; + case 0x44: + case 0x46: + case 0x4a: + gr_def(ctx, 0x403440, 0x00003010); + break; + case 0x41: + case 0x42: + case 0x43: + case 0x4c: + case 0x4e: + case 0x67: + default: + gr_def(ctx, 0x403440, 0x00001010); + break; + } + break; + } +} + +static void +nv40_gr_construct_state3d(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i; + + if (device->chipset == 0x40) { + cp_ctx(ctx, 0x401880, 51); + gr_def(ctx, 0x401940, 0x00000100); + } else + if (device->chipset == 0x46 || device->chipset == 0x47 || + device->chipset == 0x49 || device->chipset == 0x4b) { + cp_ctx(ctx, 0x401880, 32); + for (i = 0; i < 16; i++) + gr_def(ctx, 0x401880 + (i * 4), 0x00000111); + if (device->chipset == 0x46) + cp_ctx(ctx, 0x401900, 16); + cp_ctx(ctx, 0x401940, 3); + } + cp_ctx(ctx, 0x40194c, 18); + gr_def(ctx, 0x401954, 0x00000111); + gr_def(ctx, 0x401958, 0x00080060); + gr_def(ctx, 0x401974, 0x00000080); + gr_def(ctx, 0x401978, 0xffff0000); + gr_def(ctx, 0x40197c, 0x00000001); + gr_def(ctx, 0x401990, 0x46400000); + if (device->chipset == 0x40) { + cp_ctx(ctx, 0x4019a0, 2); + cp_ctx(ctx, 0x4019ac, 5); + } else { + cp_ctx(ctx, 0x4019a0, 1); + cp_ctx(ctx, 0x4019b4, 3); + } + gr_def(ctx, 0x4019bc, 0xffff0000); + switch (device->chipset) { + case 0x46: + case 0x47: + case 0x49: + case 0x4b: + cp_ctx(ctx, 0x4019c0, 18); + for (i = 0; i < 16; i++) + gr_def(ctx, 0x4019c0 + (i * 4), 0x88888888); + break; + } + cp_ctx(ctx, 0x401a08, 8); + gr_def(ctx, 0x401a10, 0x0fff0000); + gr_def(ctx, 0x401a14, 0x0fff0000); + gr_def(ctx, 0x401a1c, 0x00011100); + cp_ctx(ctx, 0x401a2c, 4); + cp_ctx(ctx, 0x401a44, 26); + for (i = 0; i < 16; i++) + gr_def(ctx, 0x401a44 + (i * 4), 0x07ff0000); + gr_def(ctx, 0x401a8c, 0x4b7fffff); + if (device->chipset == 0x40) { + cp_ctx(ctx, 0x401ab8, 3); + } else { + cp_ctx(ctx, 0x401ab8, 1); + cp_ctx(ctx, 0x401ac0, 1); + } + cp_ctx(ctx, 0x401ad0, 8); + gr_def(ctx, 0x401ad0, 0x30201000); + gr_def(ctx, 0x401ad4, 0x70605040); + gr_def(ctx, 0x401ad8, 0xb8a89888); + gr_def(ctx, 0x401adc, 0xf8e8d8c8); + cp_ctx(ctx, 0x401b10, device->chipset == 0x40 ? 2 : 1); + gr_def(ctx, 0x401b10, 0x40100000); + cp_ctx(ctx, 0x401b18, device->chipset == 0x40 ? 6 : 5); + gr_def(ctx, 0x401b28, device->chipset == 0x40 ? + 0x00000004 : 0x00000000); + cp_ctx(ctx, 0x401b30, 25); + gr_def(ctx, 0x401b34, 0x0000ffff); + gr_def(ctx, 0x401b68, 0x435185d6); + gr_def(ctx, 0x401b6c, 0x2155b699); + gr_def(ctx, 0x401b70, 0xfedcba98); + gr_def(ctx, 0x401b74, 0x00000098); + gr_def(ctx, 0x401b84, 0xffffffff); + gr_def(ctx, 0x401b88, 0x00ff7000); + gr_def(ctx, 0x401b8c, 0x0000ffff); + if (device->chipset != 0x44 && device->chipset != 0x4a && + device->chipset != 0x4e) + cp_ctx(ctx, 0x401b94, 1); + cp_ctx(ctx, 0x401b98, 8); + gr_def(ctx, 0x401b9c, 0x00ff0000); + cp_ctx(ctx, 0x401bc0, 9); + gr_def(ctx, 0x401be0, 0x00ffff00); + cp_ctx(ctx, 0x401c00, 192); + for (i = 0; i < 16; i++) { /* fragment texture units */ + gr_def(ctx, 0x401c40 + (i * 4), 0x00018488); + gr_def(ctx, 0x401c80 + (i * 4), 0x00028202); + gr_def(ctx, 0x401d00 + (i * 4), 0x0000aae4); + gr_def(ctx, 0x401d40 + (i * 4), 0x01012000); + gr_def(ctx, 0x401d80 + (i * 4), 0x00080008); + gr_def(ctx, 0x401e00 + (i * 4), 0x00100008); + } + for (i = 0; i < 4; i++) { /* vertex texture units */ + gr_def(ctx, 0x401e90 + (i * 4), 0x0001bc80); + gr_def(ctx, 0x401ea0 + (i * 4), 0x00000202); + gr_def(ctx, 0x401ec0 + (i * 4), 0x00000008); + gr_def(ctx, 0x401ee0 + (i * 4), 0x00080008); + } + cp_ctx(ctx, 0x400f5c, 3); + gr_def(ctx, 0x400f5c, 0x00000002); + cp_ctx(ctx, 0x400f84, 1); +} + +static void +nv40_gr_construct_state3d_2(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i; + + cp_ctx(ctx, 0x402000, 1); + cp_ctx(ctx, 0x402404, device->chipset == 0x40 ? 1 : 2); + switch (device->chipset) { + case 0x40: + gr_def(ctx, 0x402404, 0x00000001); + break; + case 0x4c: + case 0x4e: + case 0x67: + gr_def(ctx, 0x402404, 0x00000020); + break; + case 0x46: + case 0x49: + case 0x4b: + gr_def(ctx, 0x402404, 0x00000421); + break; + default: + gr_def(ctx, 0x402404, 0x00000021); + } + if (device->chipset != 0x40) + gr_def(ctx, 0x402408, 0x030c30c3); + switch (device->chipset) { + case 0x44: + case 0x46: + case 0x4a: + case 0x4c: + case 0x4e: + case 0x67: + cp_ctx(ctx, 0x402440, 1); + gr_def(ctx, 0x402440, 0x00011001); + break; + default: + break; + } + cp_ctx(ctx, 0x402480, device->chipset == 0x40 ? 8 : 9); + gr_def(ctx, 0x402488, 0x3e020200); + gr_def(ctx, 0x40248c, 0x00ffffff); + switch (device->chipset) { + case 0x40: + gr_def(ctx, 0x402490, 0x60103f00); + break; + case 0x47: + gr_def(ctx, 0x402490, 0x40103f00); + break; + case 0x41: + case 0x42: + case 0x49: + case 0x4b: + gr_def(ctx, 0x402490, 0x20103f00); + break; + default: + gr_def(ctx, 0x402490, 0x0c103f00); + break; + } + gr_def(ctx, 0x40249c, device->chipset <= 0x43 ? + 0x00020000 : 0x00040000); + cp_ctx(ctx, 0x402500, 31); + gr_def(ctx, 0x402530, 0x00008100); + if (device->chipset == 0x40) + cp_ctx(ctx, 0x40257c, 6); + cp_ctx(ctx, 0x402594, 16); + cp_ctx(ctx, 0x402800, 17); + gr_def(ctx, 0x402800, 0x00000001); + switch (device->chipset) { + case 0x47: + case 0x49: + case 0x4b: + cp_ctx(ctx, 0x402864, 1); + gr_def(ctx, 0x402864, 0x00001001); + cp_ctx(ctx, 0x402870, 3); + gr_def(ctx, 0x402878, 0x00000003); + if (device->chipset != 0x47) { /* belong at end!! */ + cp_ctx(ctx, 0x402900, 1); + cp_ctx(ctx, 0x402940, 1); + cp_ctx(ctx, 0x402980, 1); + cp_ctx(ctx, 0x4029c0, 1); + cp_ctx(ctx, 0x402a00, 1); + cp_ctx(ctx, 0x402a40, 1); + cp_ctx(ctx, 0x402a80, 1); + cp_ctx(ctx, 0x402ac0, 1); + } + break; + case 0x40: + cp_ctx(ctx, 0x402844, 1); + gr_def(ctx, 0x402844, 0x00000001); + cp_ctx(ctx, 0x402850, 1); + break; + default: + cp_ctx(ctx, 0x402844, 1); + gr_def(ctx, 0x402844, 0x00001001); + cp_ctx(ctx, 0x402850, 2); + gr_def(ctx, 0x402854, 0x00000003); + break; + } + + cp_ctx(ctx, 0x402c00, 4); + gr_def(ctx, 0x402c00, device->chipset == 0x40 ? + 0x80800001 : 0x00888001); + switch (device->chipset) { + case 0x47: + case 0x49: + case 0x4b: + cp_ctx(ctx, 0x402c20, 40); + for (i = 0; i < 32; i++) + gr_def(ctx, 0x402c40 + (i * 4), 0xffffffff); + cp_ctx(ctx, 0x4030b8, 13); + gr_def(ctx, 0x4030dc, 0x00000005); + gr_def(ctx, 0x4030e8, 0x0000ffff); + break; + default: + cp_ctx(ctx, 0x402c10, 4); + if (device->chipset == 0x40) + cp_ctx(ctx, 0x402c20, 36); + else + if (device->chipset <= 0x42) + cp_ctx(ctx, 0x402c20, 24); + else + if (device->chipset <= 0x4a) + cp_ctx(ctx, 0x402c20, 16); + else + cp_ctx(ctx, 0x402c20, 8); + cp_ctx(ctx, 0x402cb0, device->chipset == 0x40 ? 12 : 13); + gr_def(ctx, 0x402cd4, 0x00000005); + if (device->chipset != 0x40) + gr_def(ctx, 0x402ce0, 0x0000ffff); + break; + } + + cp_ctx(ctx, 0x403400, device->chipset == 0x40 ? 4 : 3); + cp_ctx(ctx, 0x403410, device->chipset == 0x40 ? 4 : 3); + cp_ctx(ctx, 0x403420, nv40_gr_vs_count(ctx->device)); + for (i = 0; i < nv40_gr_vs_count(ctx->device); i++) + gr_def(ctx, 0x403420 + (i * 4), 0x00005555); + + if (device->chipset != 0x40) { + cp_ctx(ctx, 0x403600, 1); + gr_def(ctx, 0x403600, 0x00000001); + } + cp_ctx(ctx, 0x403800, 1); + + cp_ctx(ctx, 0x403c18, 1); + gr_def(ctx, 0x403c18, 0x00000001); + switch (device->chipset) { + case 0x46: + case 0x47: + case 0x49: + case 0x4b: + cp_ctx(ctx, 0x405018, 1); + gr_def(ctx, 0x405018, 0x08e00001); + cp_ctx(ctx, 0x405c24, 1); + gr_def(ctx, 0x405c24, 0x000e3000); + break; + } + if (device->chipset != 0x4e) + cp_ctx(ctx, 0x405800, 11); + cp_ctx(ctx, 0x407000, 1); +} + +static void +nv40_gr_construct_state3d_3(struct nvkm_grctx *ctx) +{ + int len = nv44_gr_class(ctx->device) ? 0x0084 : 0x0684; + + cp_out (ctx, 0x300000); + cp_lsr (ctx, len - 4); + cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_swap_state3d_3_is_save); + cp_lsr (ctx, len); + cp_name(ctx, cp_swap_state3d_3_is_save); + cp_out (ctx, 0x800001); + + ctx->ctxvals_pos += len; +} + +static void +nv40_gr_construct_shader(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + struct nvkm_gpuobj *obj = ctx->data; + int vs, vs_nr, vs_len, vs_nr_b0, vs_nr_b1, b0_offset, b1_offset; + int offset, i; + + vs_nr = nv40_gr_vs_count(ctx->device); + vs_nr_b0 = 363; + vs_nr_b1 = device->chipset == 0x40 ? 128 : 64; + if (device->chipset == 0x40) { + b0_offset = 0x2200/4; /* 33a0 */ + b1_offset = 0x55a0/4; /* 1500 */ + vs_len = 0x6aa0/4; + } else + if (device->chipset == 0x41 || device->chipset == 0x42) { + b0_offset = 0x2200/4; /* 2200 */ + b1_offset = 0x4400/4; /* 0b00 */ + vs_len = 0x4f00/4; + } else { + b0_offset = 0x1d40/4; /* 2200 */ + b1_offset = 0x3f40/4; /* 0b00 : 0a40 */ + vs_len = nv44_gr_class(device) ? 0x4980/4 : 0x4a40/4; + } + + cp_lsr(ctx, vs_len * vs_nr + 0x300/4); + cp_out(ctx, nv44_gr_class(device) ? 0x800029 : 0x800041); + + offset = ctx->ctxvals_pos; + ctx->ctxvals_pos += (0x0300/4 + (vs_nr * vs_len)); + + if (ctx->mode != NVKM_GRCTX_VALS) + return; + + offset += 0x0280/4; + for (i = 0; i < 16; i++, offset += 2) + nvkm_wo32(obj, offset * 4, 0x3f800000); + + for (vs = 0; vs < vs_nr; vs++, offset += vs_len) { + for (i = 0; i < vs_nr_b0 * 6; i += 6) + nvkm_wo32(obj, (offset + b0_offset + i) * 4, 0x00000001); + for (i = 0; i < vs_nr_b1 * 4; i += 4) + nvkm_wo32(obj, (offset + b1_offset + i) * 4, 0x3f800000); + } +} + +static void +nv40_grctx_generate(struct nvkm_grctx *ctx) +{ + /* decide whether we're loading/unloading the context */ + cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save); + cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save); + + cp_name(ctx, cp_check_load); + cp_bra (ctx, AUTO_LOAD, PENDING, cp_setup_auto_load); + cp_bra (ctx, USER_LOAD, PENDING, cp_setup_load); + cp_bra (ctx, ALWAYS, TRUE, cp_exit); + + /* setup for context load */ + cp_name(ctx, cp_setup_auto_load); + cp_wait(ctx, STATUS, IDLE); + cp_out (ctx, CP_NEXT_TO_SWAP); + cp_name(ctx, cp_setup_load); + cp_wait(ctx, STATUS, IDLE); + cp_set (ctx, SWAP_DIRECTION, LOAD); + cp_out (ctx, 0x00910880); /* ?? */ + cp_out (ctx, 0x00901ffe); /* ?? */ + cp_out (ctx, 0x01940000); /* ?? */ + cp_lsr (ctx, 0x20); + cp_out (ctx, 0x0060000b); /* ?? */ + cp_wait(ctx, UNK57, CLEAR); + cp_out (ctx, 0x0060000c); /* ?? */ + cp_bra (ctx, ALWAYS, TRUE, cp_swap_state); + + /* setup for context save */ + cp_name(ctx, cp_setup_save); + cp_set (ctx, SWAP_DIRECTION, SAVE); + + /* general PGRAPH state */ + cp_name(ctx, cp_swap_state); + cp_pos (ctx, 0x00020/4); + nv40_gr_construct_general(ctx); + cp_wait(ctx, STATUS, IDLE); + + /* 3D state, block 1 */ + cp_bra (ctx, UNK54, CLEAR, cp_prepare_exit); + nv40_gr_construct_state3d(ctx); + cp_wait(ctx, STATUS, IDLE); + + /* 3D state, block 2 */ + nv40_gr_construct_state3d_2(ctx); + + /* Some other block of "random" state */ + nv40_gr_construct_state3d_3(ctx); + + /* Per-vertex shader state */ + cp_pos (ctx, ctx->ctxvals_pos); + nv40_gr_construct_shader(ctx); + + /* pre-exit state updates */ + cp_name(ctx, cp_prepare_exit); + cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_check_load); + cp_bra (ctx, USER_SAVE, PENDING, cp_exit); + cp_out (ctx, CP_NEXT_TO_CURRENT); + + cp_name(ctx, cp_exit); + cp_set (ctx, USER_SAVE, NOT_PENDING); + cp_set (ctx, USER_LOAD, NOT_PENDING); + cp_out (ctx, CP_END); +} + +void +nv40_grctx_fill(struct nvkm_device *device, struct nvkm_gpuobj *mem) +{ + nv40_grctx_generate(&(struct nvkm_grctx) { + .device = device, + .mode = NVKM_GRCTX_VALS, + .data = mem, + }); +} + +int +nv40_grctx_init(struct nvkm_device *device, u32 *size) +{ + u32 *ctxprog = kmalloc(256 * 4, GFP_KERNEL), i; + struct nvkm_grctx ctx = { + .device = device, + .mode = NVKM_GRCTX_PROG, + .ucode = ctxprog, + .ctxprog_max = 256, + }; + + if (!ctxprog) + return -ENOMEM; + + nv40_grctx_generate(&ctx); + + nvkm_wr32(device, 0x400324, 0); + for (i = 0; i < ctx.ctxprog_len; i++) + nvkm_wr32(device, 0x400328, ctxprog[i]); + *size = ctx.ctxvals_pos * 4; + + kfree(ctxprog); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h new file mode 100644 index 000000000..7917567ad --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv40.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_GRCTX_H__ +#define __NVKM_GRCTX_H__ +#include + +struct nvkm_grctx { + struct nvkm_device *device; + + enum { + NVKM_GRCTX_PROG, + NVKM_GRCTX_VALS + } mode; + u32 *ucode; + struct nvkm_gpuobj *data; + + u32 ctxprog_max; + u32 ctxprog_len; + u32 ctxprog_reg; + int ctxprog_label[32]; + u32 ctxvals_pos; + u32 ctxvals_base; +}; + +static inline void +cp_out(struct nvkm_grctx *ctx, u32 inst) +{ + u32 *ctxprog = ctx->ucode; + + if (ctx->mode != NVKM_GRCTX_PROG) + return; + + BUG_ON(ctx->ctxprog_len == ctx->ctxprog_max); + ctxprog[ctx->ctxprog_len++] = inst; +} + +static inline void +cp_lsr(struct nvkm_grctx *ctx, u32 val) +{ + cp_out(ctx, CP_LOAD_SR | val); +} + +static inline void +cp_ctx(struct nvkm_grctx *ctx, u32 reg, u32 length) +{ + ctx->ctxprog_reg = (reg - 0x00400000) >> 2; + + ctx->ctxvals_base = ctx->ctxvals_pos; + ctx->ctxvals_pos = ctx->ctxvals_base + length; + + if (length > (CP_CTX_COUNT >> CP_CTX_COUNT_SHIFT)) { + cp_lsr(ctx, length); + length = 0; + } + + cp_out(ctx, CP_CTX | (length << CP_CTX_COUNT_SHIFT) | ctx->ctxprog_reg); +} + +static inline void +cp_name(struct nvkm_grctx *ctx, int name) +{ + u32 *ctxprog = ctx->ucode; + int i; + + if (ctx->mode != NVKM_GRCTX_PROG) + return; + + ctx->ctxprog_label[name] = ctx->ctxprog_len; + for (i = 0; i < ctx->ctxprog_len; i++) { + if ((ctxprog[i] & 0xfff00000) != 0xff400000) + continue; + if ((ctxprog[i] & CP_BRA_IP) != ((name) << CP_BRA_IP_SHIFT)) + continue; + ctxprog[i] = (ctxprog[i] & 0x00ff00ff) | + (ctx->ctxprog_len << CP_BRA_IP_SHIFT); + } +} + +static inline void +_cp_bra(struct nvkm_grctx *ctx, u32 mod, int flag, int state, int name) +{ + int ip = 0; + + if (mod != 2) { + ip = ctx->ctxprog_label[name] << CP_BRA_IP_SHIFT; + if (ip == 0) + ip = 0xff000000 | (name << CP_BRA_IP_SHIFT); + } + + cp_out(ctx, CP_BRA | (mod << 18) | ip | flag | + (state ? 0 : CP_BRA_IF_CLEAR)); +} +#define cp_bra(c, f, s, n) _cp_bra((c), 0, CP_FLAG_##f, CP_FLAG_##f##_##s, n) +#define cp_cal(c, f, s, n) _cp_bra((c), 1, CP_FLAG_##f, CP_FLAG_##f##_##s, n) +#define cp_ret(c, f, s) _cp_bra((c), 2, CP_FLAG_##f, CP_FLAG_##f##_##s, 0) + +static inline void +_cp_wait(struct nvkm_grctx *ctx, int flag, int state) +{ + cp_out(ctx, CP_WAIT | flag | (state ? CP_WAIT_SET : 0)); +} +#define cp_wait(c, f, s) _cp_wait((c), CP_FLAG_##f, CP_FLAG_##f##_##s) + +static inline void +_cp_set(struct nvkm_grctx *ctx, int flag, int state) +{ + cp_out(ctx, CP_SET | flag | (state ? CP_SET_1 : 0)); +} +#define cp_set(c, f, s) _cp_set((c), CP_FLAG_##f, CP_FLAG_##f##_##s) + +static inline void +cp_pos(struct nvkm_grctx *ctx, int offset) +{ + ctx->ctxvals_pos = offset; + ctx->ctxvals_base = ctx->ctxvals_pos; + + cp_lsr(ctx, ctx->ctxvals_pos); + cp_out(ctx, CP_SET_CONTEXT_POINTER); +} + +static inline void +gr_def(struct nvkm_grctx *ctx, u32 reg, u32 val) +{ + if (ctx->mode != NVKM_GRCTX_VALS) + return; + + reg = (reg - 0x00400000) / 4; + reg = (reg - ctx->ctxprog_reg) + ctx->ctxvals_base; + + nvkm_wo32(ctx->data, reg * 4, val); +} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c new file mode 100644 index 000000000..c8bb9191f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxnv50.c @@ -0,0 +1,3347 @@ +/* + * Copyright 2009 Marcin Kościelnicki + * + * 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. + */ + +#define CP_FLAG_CLEAR 0 +#define CP_FLAG_SET 1 +#define CP_FLAG_SWAP_DIRECTION ((0 * 32) + 0) +#define CP_FLAG_SWAP_DIRECTION_LOAD 0 +#define CP_FLAG_SWAP_DIRECTION_SAVE 1 +#define CP_FLAG_UNK01 ((0 * 32) + 1) +#define CP_FLAG_UNK01_CLEAR 0 +#define CP_FLAG_UNK01_SET 1 +#define CP_FLAG_UNK03 ((0 * 32) + 3) +#define CP_FLAG_UNK03_CLEAR 0 +#define CP_FLAG_UNK03_SET 1 +#define CP_FLAG_USER_SAVE ((0 * 32) + 5) +#define CP_FLAG_USER_SAVE_NOT_PENDING 0 +#define CP_FLAG_USER_SAVE_PENDING 1 +#define CP_FLAG_USER_LOAD ((0 * 32) + 6) +#define CP_FLAG_USER_LOAD_NOT_PENDING 0 +#define CP_FLAG_USER_LOAD_PENDING 1 +#define CP_FLAG_UNK0B ((0 * 32) + 0xb) +#define CP_FLAG_UNK0B_CLEAR 0 +#define CP_FLAG_UNK0B_SET 1 +#define CP_FLAG_XFER_SWITCH ((0 * 32) + 0xe) +#define CP_FLAG_XFER_SWITCH_DISABLE 0 +#define CP_FLAG_XFER_SWITCH_ENABLE 1 +#define CP_FLAG_STATE ((0 * 32) + 0x1c) +#define CP_FLAG_STATE_STOPPED 0 +#define CP_FLAG_STATE_RUNNING 1 +#define CP_FLAG_UNK1D ((0 * 32) + 0x1d) +#define CP_FLAG_UNK1D_CLEAR 0 +#define CP_FLAG_UNK1D_SET 1 +#define CP_FLAG_UNK20 ((1 * 32) + 0) +#define CP_FLAG_UNK20_CLEAR 0 +#define CP_FLAG_UNK20_SET 1 +#define CP_FLAG_STATUS ((2 * 32) + 0) +#define CP_FLAG_STATUS_BUSY 0 +#define CP_FLAG_STATUS_IDLE 1 +#define CP_FLAG_AUTO_SAVE ((2 * 32) + 4) +#define CP_FLAG_AUTO_SAVE_NOT_PENDING 0 +#define CP_FLAG_AUTO_SAVE_PENDING 1 +#define CP_FLAG_AUTO_LOAD ((2 * 32) + 5) +#define CP_FLAG_AUTO_LOAD_NOT_PENDING 0 +#define CP_FLAG_AUTO_LOAD_PENDING 1 +#define CP_FLAG_NEWCTX ((2 * 32) + 10) +#define CP_FLAG_NEWCTX_BUSY 0 +#define CP_FLAG_NEWCTX_DONE 1 +#define CP_FLAG_XFER ((2 * 32) + 11) +#define CP_FLAG_XFER_IDLE 0 +#define CP_FLAG_XFER_BUSY 1 +#define CP_FLAG_ALWAYS ((2 * 32) + 13) +#define CP_FLAG_ALWAYS_FALSE 0 +#define CP_FLAG_ALWAYS_TRUE 1 +#define CP_FLAG_INTR ((2 * 32) + 15) +#define CP_FLAG_INTR_NOT_PENDING 0 +#define CP_FLAG_INTR_PENDING 1 + +#define CP_CTX 0x00100000 +#define CP_CTX_COUNT 0x000f0000 +#define CP_CTX_COUNT_SHIFT 16 +#define CP_CTX_REG 0x00003fff +#define CP_LOAD_SR 0x00200000 +#define CP_LOAD_SR_VALUE 0x000fffff +#define CP_BRA 0x00400000 +#define CP_BRA_IP 0x0001ff00 +#define CP_BRA_IP_SHIFT 8 +#define CP_BRA_IF_CLEAR 0x00000080 +#define CP_BRA_FLAG 0x0000007f +#define CP_WAIT 0x00500000 +#define CP_WAIT_SET 0x00000080 +#define CP_WAIT_FLAG 0x0000007f +#define CP_SET 0x00700000 +#define CP_SET_1 0x00000080 +#define CP_SET_FLAG 0x0000007f +#define CP_NEWCTX 0x00600004 +#define CP_NEXT_TO_SWAP 0x00600005 +#define CP_SET_CONTEXT_POINTER 0x00600006 +#define CP_SET_XFER_POINTER 0x00600007 +#define CP_ENABLE 0x00600009 +#define CP_END 0x0060000c +#define CP_NEXT_TO_CURRENT 0x0060000d +#define CP_DISABLE1 0x0090ffff +#define CP_DISABLE2 0x0091ffff +#define CP_XFER_1 0x008000ff +#define CP_XFER_2 0x008800ff +#define CP_SEEK_1 0x00c000ff +#define CP_SEEK_2 0x00c800ff + +#include "ctxnv40.h" +#include "nv50.h" + +#include + +#define IS_NVA3F(x) (((x) > 0xa0 && (x) < 0xaa) || (x) == 0xaf) +#define IS_NVAAF(x) ((x) >= 0xaa && (x) <= 0xac) + +/* + * This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's + * the GPU itself that does context-switching, but it needs a special + * microcode to do it. And it's the driver's task to supply this microcode, + * further known as ctxprog, as well as the initial context values, known + * as ctxvals. + * + * Without ctxprog, you cannot switch contexts. Not even in software, since + * the majority of context [xfer strands] isn't accessible directly. You're + * stuck with a single channel, and you also suffer all the problems resulting + * from missing ctxvals, since you cannot load them. + * + * Without ctxvals, you're stuck with PGRAPH's default context. It's enough to + * run 2d operations, but trying to utilise 3d or CUDA will just lock you up, + * since you don't have... some sort of needed setup. + * + * Nouveau will just disable acceleration if not given ctxprog + ctxvals, since + * it's too much hassle to handle no-ctxprog as a special case. + */ + +/* + * How ctxprogs work. + * + * The ctxprog is written in its own kind of microcode, with very small and + * crappy set of available commands. You upload it to a small [512 insns] + * area of memory on PGRAPH, and it'll be run when PFIFO wants PGRAPH to + * switch channel. or when the driver explicitely requests it. Stuff visible + * to ctxprog consists of: PGRAPH MMIO registers, PGRAPH context strands, + * the per-channel context save area in VRAM [known as ctxvals or grctx], + * 4 flags registers, a scratch register, two grctx pointers, plus many + * random poorly-understood details. + * + * When ctxprog runs, it's supposed to check what operations are asked of it, + * save old context if requested, optionally reset PGRAPH and switch to the + * new channel, and load the new context. Context consists of three major + * parts: subset of MMIO registers and two "xfer areas". + */ + +/* TODO: + * - document unimplemented bits compared to nvidia + * - NVAx: make a TP subroutine, use it. + * - use 0x4008fc instead of 0x1540? + */ + +enum cp_label { + cp_check_load = 1, + cp_setup_auto_load, + cp_setup_load, + cp_setup_save, + cp_swap_state, + cp_prepare_exit, + cp_exit, +}; + +static void nv50_gr_construct_mmio(struct nvkm_grctx *ctx); +static void nv50_gr_construct_xfer1(struct nvkm_grctx *ctx); +static void nv50_gr_construct_xfer2(struct nvkm_grctx *ctx); + +/* Main function: construct the ctxprog skeleton, call the other functions. */ + +static int +nv50_grctx_generate(struct nvkm_grctx *ctx) +{ + cp_set (ctx, STATE, RUNNING); + cp_set (ctx, XFER_SWITCH, ENABLE); + /* decide whether we're loading/unloading the context */ + cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save); + cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save); + + cp_name(ctx, cp_check_load); + cp_bra (ctx, AUTO_LOAD, PENDING, cp_setup_auto_load); + cp_bra (ctx, USER_LOAD, PENDING, cp_setup_load); + cp_bra (ctx, ALWAYS, TRUE, cp_prepare_exit); + + /* setup for context load */ + cp_name(ctx, cp_setup_auto_load); + cp_out (ctx, CP_DISABLE1); + cp_out (ctx, CP_DISABLE2); + cp_out (ctx, CP_ENABLE); + cp_out (ctx, CP_NEXT_TO_SWAP); + cp_set (ctx, UNK01, SET); + cp_name(ctx, cp_setup_load); + cp_out (ctx, CP_NEWCTX); + cp_wait(ctx, NEWCTX, BUSY); + cp_set (ctx, UNK1D, CLEAR); + cp_set (ctx, SWAP_DIRECTION, LOAD); + cp_bra (ctx, UNK0B, SET, cp_prepare_exit); + cp_bra (ctx, ALWAYS, TRUE, cp_swap_state); + + /* setup for context save */ + cp_name(ctx, cp_setup_save); + cp_set (ctx, UNK1D, SET); + cp_wait(ctx, STATUS, BUSY); + cp_wait(ctx, INTR, PENDING); + cp_bra (ctx, STATUS, BUSY, cp_setup_save); + cp_set (ctx, UNK01, SET); + cp_set (ctx, SWAP_DIRECTION, SAVE); + + /* general PGRAPH state */ + cp_name(ctx, cp_swap_state); + cp_set (ctx, UNK03, SET); + cp_pos (ctx, 0x00004/4); + cp_ctx (ctx, 0x400828, 1); /* needed. otherwise, flickering happens. */ + cp_pos (ctx, 0x00100/4); + nv50_gr_construct_mmio(ctx); + nv50_gr_construct_xfer1(ctx); + nv50_gr_construct_xfer2(ctx); + + cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_check_load); + + cp_set (ctx, UNK20, SET); + cp_set (ctx, SWAP_DIRECTION, SAVE); /* no idea why this is needed, but fixes at least one lockup. */ + cp_lsr (ctx, ctx->ctxvals_base); + cp_out (ctx, CP_SET_XFER_POINTER); + cp_lsr (ctx, 4); + cp_out (ctx, CP_SEEK_1); + cp_out (ctx, CP_XFER_1); + cp_wait(ctx, XFER, BUSY); + + /* pre-exit state updates */ + cp_name(ctx, cp_prepare_exit); + cp_set (ctx, UNK01, CLEAR); + cp_set (ctx, UNK03, CLEAR); + cp_set (ctx, UNK1D, CLEAR); + + cp_bra (ctx, USER_SAVE, PENDING, cp_exit); + cp_out (ctx, CP_NEXT_TO_CURRENT); + + cp_name(ctx, cp_exit); + cp_set (ctx, USER_SAVE, NOT_PENDING); + cp_set (ctx, USER_LOAD, NOT_PENDING); + cp_set (ctx, XFER_SWITCH, DISABLE); + cp_set (ctx, STATE, STOPPED); + cp_out (ctx, CP_END); + ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */ + + return 0; +} + +void +nv50_grctx_fill(struct nvkm_device *device, struct nvkm_gpuobj *mem) +{ + nv50_grctx_generate(&(struct nvkm_grctx) { + .device = device, + .mode = NVKM_GRCTX_VALS, + .data = mem, + }); +} + +int +nv50_grctx_init(struct nvkm_device *device, u32 *size) +{ + u32 *ctxprog = kmalloc(512 * 4, GFP_KERNEL), i; + struct nvkm_grctx ctx = { + .device = device, + .mode = NVKM_GRCTX_PROG, + .ucode = ctxprog, + .ctxprog_max = 512, + }; + + if (!ctxprog) + return -ENOMEM; + nv50_grctx_generate(&ctx); + + nvkm_wr32(device, 0x400324, 0); + for (i = 0; i < ctx.ctxprog_len; i++) + nvkm_wr32(device, 0x400328, ctxprog[i]); + *size = ctx.ctxvals_pos * 4; + kfree(ctxprog); + return 0; +} + +/* + * Constructs MMIO part of ctxprog and ctxvals. Just a matter of knowing which + * registers to save/restore and the default values for them. + */ + +static void +nv50_gr_construct_mmio_ddata(struct nvkm_grctx *ctx); + +static void +nv50_gr_construct_mmio(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i, j; + int offset, base; + u32 units = nvkm_rd32(device, 0x1540); + + /* 0800: DISPATCH */ + cp_ctx(ctx, 0x400808, 7); + gr_def(ctx, 0x400814, 0x00000030); + cp_ctx(ctx, 0x400834, 0x32); + if (device->chipset == 0x50) { + gr_def(ctx, 0x400834, 0xff400040); + gr_def(ctx, 0x400838, 0xfff00080); + gr_def(ctx, 0x40083c, 0xfff70090); + gr_def(ctx, 0x400840, 0xffe806a8); + } + gr_def(ctx, 0x400844, 0x00000002); + if (IS_NVA3F(device->chipset)) + gr_def(ctx, 0x400894, 0x00001000); + gr_def(ctx, 0x4008e8, 0x00000003); + gr_def(ctx, 0x4008ec, 0x00001000); + if (device->chipset == 0x50) + cp_ctx(ctx, 0x400908, 0xb); + else if (device->chipset < 0xa0) + cp_ctx(ctx, 0x400908, 0xc); + else + cp_ctx(ctx, 0x400908, 0xe); + + if (device->chipset >= 0xa0) + cp_ctx(ctx, 0x400b00, 0x1); + if (IS_NVA3F(device->chipset)) { + cp_ctx(ctx, 0x400b10, 0x1); + gr_def(ctx, 0x400b10, 0x0001629d); + cp_ctx(ctx, 0x400b20, 0x1); + gr_def(ctx, 0x400b20, 0x0001629d); + } + + nv50_gr_construct_mmio_ddata(ctx); + + /* 0C00: VFETCH */ + cp_ctx(ctx, 0x400c08, 0x2); + gr_def(ctx, 0x400c08, 0x0000fe0c); + + /* 1000 */ + if (device->chipset < 0xa0) { + cp_ctx(ctx, 0x401008, 0x4); + gr_def(ctx, 0x401014, 0x00001000); + } else if (!IS_NVA3F(device->chipset)) { + cp_ctx(ctx, 0x401008, 0x5); + gr_def(ctx, 0x401018, 0x00001000); + } else { + cp_ctx(ctx, 0x401008, 0x5); + gr_def(ctx, 0x401018, 0x00004000); + } + + /* 1400 */ + cp_ctx(ctx, 0x401400, 0x8); + cp_ctx(ctx, 0x401424, 0x3); + if (device->chipset == 0x50) + gr_def(ctx, 0x40142c, 0x0001fd87); + else + gr_def(ctx, 0x40142c, 0x00000187); + cp_ctx(ctx, 0x401540, 0x5); + gr_def(ctx, 0x401550, 0x00001018); + + /* 1800: STREAMOUT */ + cp_ctx(ctx, 0x401814, 0x1); + gr_def(ctx, 0x401814, 0x000000ff); + if (device->chipset == 0x50) { + cp_ctx(ctx, 0x40181c, 0xe); + gr_def(ctx, 0x401850, 0x00000004); + } else if (device->chipset < 0xa0) { + cp_ctx(ctx, 0x40181c, 0xf); + gr_def(ctx, 0x401854, 0x00000004); + } else { + cp_ctx(ctx, 0x40181c, 0x13); + gr_def(ctx, 0x401864, 0x00000004); + } + + /* 1C00 */ + cp_ctx(ctx, 0x401c00, 0x1); + switch (device->chipset) { + case 0x50: + gr_def(ctx, 0x401c00, 0x0001005f); + break; + case 0x84: + case 0x86: + case 0x94: + gr_def(ctx, 0x401c00, 0x044d00df); + break; + case 0x92: + case 0x96: + case 0x98: + case 0xa0: + case 0xaa: + case 0xac: + gr_def(ctx, 0x401c00, 0x042500df); + break; + case 0xa3: + case 0xa5: + case 0xa8: + case 0xaf: + gr_def(ctx, 0x401c00, 0x142500df); + break; + } + + /* 2000 */ + + /* 2400 */ + cp_ctx(ctx, 0x402400, 0x1); + if (device->chipset == 0x50) + cp_ctx(ctx, 0x402408, 0x1); + else + cp_ctx(ctx, 0x402408, 0x2); + gr_def(ctx, 0x402408, 0x00000600); + + /* 2800: CSCHED */ + cp_ctx(ctx, 0x402800, 0x1); + if (device->chipset == 0x50) + gr_def(ctx, 0x402800, 0x00000006); + + /* 2C00: ZCULL */ + cp_ctx(ctx, 0x402c08, 0x6); + if (device->chipset != 0x50) + gr_def(ctx, 0x402c14, 0x01000000); + gr_def(ctx, 0x402c18, 0x000000ff); + if (device->chipset == 0x50) + cp_ctx(ctx, 0x402ca0, 0x1); + else + cp_ctx(ctx, 0x402ca0, 0x2); + if (device->chipset < 0xa0) + gr_def(ctx, 0x402ca0, 0x00000400); + else if (!IS_NVA3F(device->chipset)) + gr_def(ctx, 0x402ca0, 0x00000800); + else + gr_def(ctx, 0x402ca0, 0x00000400); + cp_ctx(ctx, 0x402cac, 0x4); + + /* 3000: ENG2D */ + cp_ctx(ctx, 0x403004, 0x1); + gr_def(ctx, 0x403004, 0x00000001); + + /* 3400 */ + if (device->chipset >= 0xa0) { + cp_ctx(ctx, 0x403404, 0x1); + gr_def(ctx, 0x403404, 0x00000001); + } + + /* 5000: CCACHE */ + cp_ctx(ctx, 0x405000, 0x1); + switch (device->chipset) { + case 0x50: + gr_def(ctx, 0x405000, 0x00300080); + break; + case 0x84: + case 0xa0: + case 0xa3: + case 0xa5: + case 0xa8: + case 0xaa: + case 0xac: + case 0xaf: + gr_def(ctx, 0x405000, 0x000e0080); + break; + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + gr_def(ctx, 0x405000, 0x00000080); + break; + } + cp_ctx(ctx, 0x405014, 0x1); + gr_def(ctx, 0x405014, 0x00000004); + cp_ctx(ctx, 0x40501c, 0x1); + cp_ctx(ctx, 0x405024, 0x1); + cp_ctx(ctx, 0x40502c, 0x1); + + /* 6000? */ + if (device->chipset == 0x50) + cp_ctx(ctx, 0x4063e0, 0x1); + + /* 6800: M2MF */ + if (device->chipset < 0x90) { + cp_ctx(ctx, 0x406814, 0x2b); + gr_def(ctx, 0x406818, 0x00000f80); + gr_def(ctx, 0x406860, 0x007f0080); + gr_def(ctx, 0x40689c, 0x007f0080); + } else { + cp_ctx(ctx, 0x406814, 0x4); + if (device->chipset == 0x98) + gr_def(ctx, 0x406818, 0x00000f80); + else + gr_def(ctx, 0x406818, 0x00001f80); + if (IS_NVA3F(device->chipset)) + gr_def(ctx, 0x40681c, 0x00000030); + cp_ctx(ctx, 0x406830, 0x3); + } + + /* 7000: per-ROP group state */ + for (i = 0; i < 8; i++) { + if (units & (1<<(i+16))) { + cp_ctx(ctx, 0x407000 + (i<<8), 3); + if (device->chipset == 0x50) + gr_def(ctx, 0x407000 + (i<<8), 0x1b74f820); + else if (device->chipset != 0xa5) + gr_def(ctx, 0x407000 + (i<<8), 0x3b74f821); + else + gr_def(ctx, 0x407000 + (i<<8), 0x7b74f821); + gr_def(ctx, 0x407004 + (i<<8), 0x89058001); + + if (device->chipset == 0x50) { + cp_ctx(ctx, 0x407010 + (i<<8), 1); + } else if (device->chipset < 0xa0) { + cp_ctx(ctx, 0x407010 + (i<<8), 2); + gr_def(ctx, 0x407010 + (i<<8), 0x00001000); + gr_def(ctx, 0x407014 + (i<<8), 0x0000001f); + } else { + cp_ctx(ctx, 0x407010 + (i<<8), 3); + gr_def(ctx, 0x407010 + (i<<8), 0x00001000); + if (device->chipset != 0xa5) + gr_def(ctx, 0x407014 + (i<<8), 0x000000ff); + else + gr_def(ctx, 0x407014 + (i<<8), 0x000001ff); + } + + cp_ctx(ctx, 0x407080 + (i<<8), 4); + if (device->chipset != 0xa5) + gr_def(ctx, 0x407080 + (i<<8), 0x027c10fa); + else + gr_def(ctx, 0x407080 + (i<<8), 0x827c10fa); + if (device->chipset == 0x50) + gr_def(ctx, 0x407084 + (i<<8), 0x000000c0); + else + gr_def(ctx, 0x407084 + (i<<8), 0x400000c0); + gr_def(ctx, 0x407088 + (i<<8), 0xb7892080); + + if (device->chipset < 0xa0) + cp_ctx(ctx, 0x407094 + (i<<8), 1); + else if (!IS_NVA3F(device->chipset)) + cp_ctx(ctx, 0x407094 + (i<<8), 3); + else { + cp_ctx(ctx, 0x407094 + (i<<8), 4); + gr_def(ctx, 0x4070a0 + (i<<8), 1); + } + } + } + + cp_ctx(ctx, 0x407c00, 0x3); + if (device->chipset < 0x90) + gr_def(ctx, 0x407c00, 0x00010040); + else if (device->chipset < 0xa0) + gr_def(ctx, 0x407c00, 0x00390040); + else + gr_def(ctx, 0x407c00, 0x003d0040); + gr_def(ctx, 0x407c08, 0x00000022); + if (device->chipset >= 0xa0) { + cp_ctx(ctx, 0x407c10, 0x3); + cp_ctx(ctx, 0x407c20, 0x1); + cp_ctx(ctx, 0x407c2c, 0x1); + } + + if (device->chipset < 0xa0) { + cp_ctx(ctx, 0x407d00, 0x9); + } else { + cp_ctx(ctx, 0x407d00, 0x15); + } + if (device->chipset == 0x98) + gr_def(ctx, 0x407d08, 0x00380040); + else { + if (device->chipset < 0x90) + gr_def(ctx, 0x407d08, 0x00010040); + else if (device->chipset < 0xa0) + gr_def(ctx, 0x407d08, 0x00390040); + else { + if (device->fb->ram->type != NVKM_RAM_TYPE_GDDR5) + gr_def(ctx, 0x407d08, 0x003d0040); + else + gr_def(ctx, 0x407d08, 0x003c0040); + } + gr_def(ctx, 0x407d0c, 0x00000022); + } + + /* 8000+: per-TP state */ + for (i = 0; i < 10; i++) { + if (units & (1<chipset < 0xa0) + base = 0x408000 + (i<<12); + else + base = 0x408000 + (i<<11); + if (device->chipset < 0xa0) + offset = base + 0xc00; + else + offset = base + 0x80; + cp_ctx(ctx, offset + 0x00, 1); + gr_def(ctx, offset + 0x00, 0x0000ff0a); + cp_ctx(ctx, offset + 0x08, 1); + + /* per-MP state */ + for (j = 0; j < (device->chipset < 0xa0 ? 2 : 4); j++) { + if (!(units & (1 << (j+24)))) continue; + if (device->chipset < 0xa0) + offset = base + 0x200 + (j<<7); + else + offset = base + 0x100 + (j<<7); + cp_ctx(ctx, offset, 0x20); + gr_def(ctx, offset + 0x00, 0x01800000); + gr_def(ctx, offset + 0x04, 0x00160000); + gr_def(ctx, offset + 0x08, 0x01800000); + gr_def(ctx, offset + 0x18, 0x0003ffff); + switch (device->chipset) { + case 0x50: + gr_def(ctx, offset + 0x1c, 0x00080000); + break; + case 0x84: + gr_def(ctx, offset + 0x1c, 0x00880000); + break; + case 0x86: + gr_def(ctx, offset + 0x1c, 0x018c0000); + break; + case 0x92: + case 0x96: + case 0x98: + gr_def(ctx, offset + 0x1c, 0x118c0000); + break; + case 0x94: + gr_def(ctx, offset + 0x1c, 0x10880000); + break; + case 0xa0: + case 0xa5: + gr_def(ctx, offset + 0x1c, 0x310c0000); + break; + case 0xa3: + case 0xa8: + case 0xaa: + case 0xac: + case 0xaf: + gr_def(ctx, offset + 0x1c, 0x300c0000); + break; + } + gr_def(ctx, offset + 0x40, 0x00010401); + if (device->chipset == 0x50) + gr_def(ctx, offset + 0x48, 0x00000040); + else + gr_def(ctx, offset + 0x48, 0x00000078); + gr_def(ctx, offset + 0x50, 0x000000bf); + gr_def(ctx, offset + 0x58, 0x00001210); + if (device->chipset == 0x50) + gr_def(ctx, offset + 0x5c, 0x00000080); + else + gr_def(ctx, offset + 0x5c, 0x08000080); + if (device->chipset >= 0xa0) + gr_def(ctx, offset + 0x68, 0x0000003e); + } + + if (device->chipset < 0xa0) + cp_ctx(ctx, base + 0x300, 0x4); + else + cp_ctx(ctx, base + 0x300, 0x5); + if (device->chipset == 0x50) + gr_def(ctx, base + 0x304, 0x00007070); + else if (device->chipset < 0xa0) + gr_def(ctx, base + 0x304, 0x00027070); + else if (!IS_NVA3F(device->chipset)) + gr_def(ctx, base + 0x304, 0x01127070); + else + gr_def(ctx, base + 0x304, 0x05127070); + + if (device->chipset < 0xa0) + cp_ctx(ctx, base + 0x318, 1); + else + cp_ctx(ctx, base + 0x320, 1); + if (device->chipset == 0x50) + gr_def(ctx, base + 0x318, 0x0003ffff); + else if (device->chipset < 0xa0) + gr_def(ctx, base + 0x318, 0x03ffffff); + else + gr_def(ctx, base + 0x320, 0x07ffffff); + + if (device->chipset < 0xa0) + cp_ctx(ctx, base + 0x324, 5); + else + cp_ctx(ctx, base + 0x328, 4); + + if (device->chipset < 0xa0) { + cp_ctx(ctx, base + 0x340, 9); + offset = base + 0x340; + } else if (!IS_NVA3F(device->chipset)) { + cp_ctx(ctx, base + 0x33c, 0xb); + offset = base + 0x344; + } else { + cp_ctx(ctx, base + 0x33c, 0xd); + offset = base + 0x344; + } + gr_def(ctx, offset + 0x0, 0x00120407); + gr_def(ctx, offset + 0x4, 0x05091507); + if (device->chipset == 0x84) + gr_def(ctx, offset + 0x8, 0x05100202); + else + gr_def(ctx, offset + 0x8, 0x05010202); + gr_def(ctx, offset + 0xc, 0x00030201); + if (device->chipset == 0xa3) + cp_ctx(ctx, base + 0x36c, 1); + + cp_ctx(ctx, base + 0x400, 2); + gr_def(ctx, base + 0x404, 0x00000040); + cp_ctx(ctx, base + 0x40c, 2); + gr_def(ctx, base + 0x40c, 0x0d0c0b0a); + gr_def(ctx, base + 0x410, 0x00141210); + + if (device->chipset < 0xa0) + offset = base + 0x800; + else + offset = base + 0x500; + cp_ctx(ctx, offset, 6); + gr_def(ctx, offset + 0x0, 0x000001f0); + gr_def(ctx, offset + 0x4, 0x00000001); + gr_def(ctx, offset + 0x8, 0x00000003); + if (device->chipset == 0x50 || IS_NVAAF(device->chipset)) + gr_def(ctx, offset + 0xc, 0x00008000); + gr_def(ctx, offset + 0x14, 0x00039e00); + cp_ctx(ctx, offset + 0x1c, 2); + if (device->chipset == 0x50) + gr_def(ctx, offset + 0x1c, 0x00000040); + else + gr_def(ctx, offset + 0x1c, 0x00000100); + gr_def(ctx, offset + 0x20, 0x00003800); + + if (device->chipset >= 0xa0) { + cp_ctx(ctx, base + 0x54c, 2); + if (!IS_NVA3F(device->chipset)) + gr_def(ctx, base + 0x54c, 0x003fe006); + else + gr_def(ctx, base + 0x54c, 0x003fe007); + gr_def(ctx, base + 0x550, 0x003fe000); + } + + if (device->chipset < 0xa0) + offset = base + 0xa00; + else + offset = base + 0x680; + cp_ctx(ctx, offset, 1); + gr_def(ctx, offset, 0x00404040); + + if (device->chipset < 0xa0) + offset = base + 0xe00; + else + offset = base + 0x700; + cp_ctx(ctx, offset, 2); + if (device->chipset < 0xa0) + gr_def(ctx, offset, 0x0077f005); + else if (device->chipset == 0xa5) + gr_def(ctx, offset, 0x6cf7f007); + else if (device->chipset == 0xa8) + gr_def(ctx, offset, 0x6cfff007); + else if (device->chipset == 0xac) + gr_def(ctx, offset, 0x0cfff007); + else + gr_def(ctx, offset, 0x0cf7f007); + if (device->chipset == 0x50) + gr_def(ctx, offset + 0x4, 0x00007fff); + else if (device->chipset < 0xa0) + gr_def(ctx, offset + 0x4, 0x003f7fff); + else + gr_def(ctx, offset + 0x4, 0x02bf7fff); + cp_ctx(ctx, offset + 0x2c, 1); + if (device->chipset == 0x50) { + cp_ctx(ctx, offset + 0x50, 9); + gr_def(ctx, offset + 0x54, 0x000003ff); + gr_def(ctx, offset + 0x58, 0x00000003); + gr_def(ctx, offset + 0x5c, 0x00000003); + gr_def(ctx, offset + 0x60, 0x000001ff); + gr_def(ctx, offset + 0x64, 0x0000001f); + gr_def(ctx, offset + 0x68, 0x0000000f); + gr_def(ctx, offset + 0x6c, 0x0000000f); + } else if (device->chipset < 0xa0) { + cp_ctx(ctx, offset + 0x50, 1); + cp_ctx(ctx, offset + 0x70, 1); + } else { + cp_ctx(ctx, offset + 0x50, 1); + cp_ctx(ctx, offset + 0x60, 5); + } + } + } +} + +static void +dd_emit(struct nvkm_grctx *ctx, int num, u32 val) { + int i; + if (val && ctx->mode == NVKM_GRCTX_VALS) { + for (i = 0; i < num; i++) + nvkm_wo32(ctx->data, 4 * (ctx->ctxvals_pos + i), val); + } + ctx->ctxvals_pos += num; +} + +static void +nv50_gr_construct_mmio_ddata(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int base, num; + base = ctx->ctxvals_pos; + + /* tesla state */ + dd_emit(ctx, 1, 0); /* 00000001 UNK0F90 */ + dd_emit(ctx, 1, 0); /* 00000001 UNK135C */ + + /* SRC_TIC state */ + dd_emit(ctx, 1, 0); /* 00000007 SRC_TILE_MODE_Z */ + dd_emit(ctx, 1, 2); /* 00000007 SRC_TILE_MODE_Y */ + dd_emit(ctx, 1, 1); /* 00000001 SRC_LINEAR #1 */ + dd_emit(ctx, 1, 0); /* 000000ff SRC_ADDRESS_HIGH */ + dd_emit(ctx, 1, 0); /* 00000001 SRC_SRGB */ + if (device->chipset >= 0x94) + dd_emit(ctx, 1, 0); /* 00000003 eng2d UNK0258 */ + dd_emit(ctx, 1, 1); /* 00000fff SRC_DEPTH */ + dd_emit(ctx, 1, 0x100); /* 0000ffff SRC_HEIGHT */ + + /* turing state */ + dd_emit(ctx, 1, 0); /* 0000000f TEXTURES_LOG2 */ + dd_emit(ctx, 1, 0); /* 0000000f SAMPLERS_LOG2 */ + dd_emit(ctx, 1, 0); /* 000000ff CB_DEF_ADDRESS_HIGH */ + dd_emit(ctx, 1, 0); /* ffffffff CB_DEF_ADDRESS_LOW */ + dd_emit(ctx, 1, 0); /* ffffffff SHARED_SIZE */ + dd_emit(ctx, 1, 2); /* ffffffff REG_MODE */ + dd_emit(ctx, 1, 1); /* 0000ffff BLOCK_ALLOC_THREADS */ + dd_emit(ctx, 1, 1); /* 00000001 LANES32 */ + dd_emit(ctx, 1, 0); /* 000000ff UNK370 */ + dd_emit(ctx, 1, 0); /* 000000ff USER_PARAM_UNK */ + dd_emit(ctx, 1, 0); /* 000000ff USER_PARAM_COUNT */ + dd_emit(ctx, 1, 1); /* 000000ff UNK384 bits 8-15 */ + dd_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */ + dd_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */ + dd_emit(ctx, 1, 0); /* 0000ffff CB_ADDR_INDEX */ + dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_X */ + dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_XMY */ + dd_emit(ctx, 1, 0); /* 00000001 BLOCKDIM_XMY_OVERFLOW */ + dd_emit(ctx, 1, 1); /* 0003ffff BLOCKDIM_XMYMZ */ + dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_Y */ + dd_emit(ctx, 1, 1); /* 0000007f BLOCKDIM_Z */ + dd_emit(ctx, 1, 4); /* 000000ff CP_REG_ALLOC_TEMP */ + dd_emit(ctx, 1, 1); /* 00000001 BLOCKDIM_DIRTY */ + if (IS_NVA3F(device->chipset)) + dd_emit(ctx, 1, 0); /* 00000003 UNK03E8 */ + dd_emit(ctx, 1, 1); /* 0000007f BLOCK_ALLOC_HALFWARPS */ + dd_emit(ctx, 1, 1); /* 00000007 LOCAL_WARPS_NO_CLAMP */ + dd_emit(ctx, 1, 7); /* 00000007 LOCAL_WARPS_LOG_ALLOC */ + dd_emit(ctx, 1, 1); /* 00000007 STACK_WARPS_NO_CLAMP */ + dd_emit(ctx, 1, 7); /* 00000007 STACK_WARPS_LOG_ALLOC */ + dd_emit(ctx, 1, 1); /* 00001fff BLOCK_ALLOC_REGSLOTS_PACKED */ + dd_emit(ctx, 1, 1); /* 00001fff BLOCK_ALLOC_REGSLOTS_STRIDED */ + dd_emit(ctx, 1, 1); /* 000007ff BLOCK_ALLOC_THREADS */ + + /* compat 2d state */ + if (device->chipset == 0x50) { + dd_emit(ctx, 4, 0); /* 0000ffff clip X, Y, W, H */ + + dd_emit(ctx, 1, 1); /* ffffffff chroma COLOR_FORMAT */ + + dd_emit(ctx, 1, 1); /* ffffffff pattern COLOR_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff pattern SHAPE */ + dd_emit(ctx, 1, 1); /* ffffffff pattern PATTERN_SELECT */ + + dd_emit(ctx, 1, 0xa); /* ffffffff surf2d SRC_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff surf2d DMA_SRC */ + dd_emit(ctx, 1, 0); /* 000000ff surf2d SRC_ADDRESS_HIGH */ + dd_emit(ctx, 1, 0); /* ffffffff surf2d SRC_ADDRESS_LOW */ + dd_emit(ctx, 1, 0x40); /* 0000ffff surf2d SRC_PITCH */ + dd_emit(ctx, 1, 0); /* 0000000f surf2d SRC_TILE_MODE_Z */ + dd_emit(ctx, 1, 2); /* 0000000f surf2d SRC_TILE_MODE_Y */ + dd_emit(ctx, 1, 0x100); /* ffffffff surf2d SRC_HEIGHT */ + dd_emit(ctx, 1, 1); /* 00000001 surf2d SRC_LINEAR */ + dd_emit(ctx, 1, 0x100); /* ffffffff surf2d SRC_WIDTH */ + + dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_B_X */ + dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_B_Y */ + dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_C_X */ + dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_C_Y */ + dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_D_X */ + dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_D_Y */ + dd_emit(ctx, 1, 1); /* ffffffff gdirect COLOR_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff gdirect OPERATION */ + dd_emit(ctx, 1, 0); /* 0000ffff gdirect POINT_X */ + dd_emit(ctx, 1, 0); /* 0000ffff gdirect POINT_Y */ + + dd_emit(ctx, 1, 0); /* 0000ffff blit SRC_Y */ + dd_emit(ctx, 1, 0); /* ffffffff blit OPERATION */ + + dd_emit(ctx, 1, 0); /* ffffffff ifc OPERATION */ + + dd_emit(ctx, 1, 0); /* ffffffff iifc INDEX_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff iifc LUT_OFFSET */ + dd_emit(ctx, 1, 4); /* ffffffff iifc COLOR_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff iifc OPERATION */ + } + + /* m2mf state */ + dd_emit(ctx, 1, 0); /* ffffffff m2mf LINE_COUNT */ + dd_emit(ctx, 1, 0); /* ffffffff m2mf LINE_LENGTH_IN */ + dd_emit(ctx, 2, 0); /* ffffffff m2mf OFFSET_IN, OFFSET_OUT */ + dd_emit(ctx, 1, 1); /* ffffffff m2mf TILING_DEPTH_OUT */ + dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_HEIGHT_OUT */ + dd_emit(ctx, 1, 0); /* ffffffff m2mf TILING_POSITION_OUT_Z */ + dd_emit(ctx, 1, 1); /* 00000001 m2mf LINEAR_OUT */ + dd_emit(ctx, 2, 0); /* 0000ffff m2mf TILING_POSITION_OUT_X, Y */ + dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_PITCH_OUT */ + dd_emit(ctx, 1, 1); /* ffffffff m2mf TILING_DEPTH_IN */ + dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_HEIGHT_IN */ + dd_emit(ctx, 1, 0); /* ffffffff m2mf TILING_POSITION_IN_Z */ + dd_emit(ctx, 1, 1); /* 00000001 m2mf LINEAR_IN */ + dd_emit(ctx, 2, 0); /* 0000ffff m2mf TILING_POSITION_IN_X, Y */ + dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_PITCH_IN */ + + /* more compat 2d state */ + if (device->chipset == 0x50) { + dd_emit(ctx, 1, 1); /* ffffffff line COLOR_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff line OPERATION */ + + dd_emit(ctx, 1, 1); /* ffffffff triangle COLOR_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff triangle OPERATION */ + + dd_emit(ctx, 1, 0); /* 0000000f sifm TILE_MODE_Z */ + dd_emit(ctx, 1, 2); /* 0000000f sifm TILE_MODE_Y */ + dd_emit(ctx, 1, 0); /* 000000ff sifm FORMAT_FILTER */ + dd_emit(ctx, 1, 1); /* 000000ff sifm FORMAT_ORIGIN */ + dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_PITCH */ + dd_emit(ctx, 1, 1); /* 00000001 sifm SRC_LINEAR */ + dd_emit(ctx, 1, 0); /* 000000ff sifm SRC_OFFSET_HIGH */ + dd_emit(ctx, 1, 0); /* ffffffff sifm SRC_OFFSET */ + dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_HEIGHT */ + dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_WIDTH */ + dd_emit(ctx, 1, 3); /* ffffffff sifm COLOR_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff sifm OPERATION */ + + dd_emit(ctx, 1, 0); /* ffffffff sifc OPERATION */ + } + + /* tesla state */ + dd_emit(ctx, 1, 0); /* 0000000f GP_TEXTURES_LOG2 */ + dd_emit(ctx, 1, 0); /* 0000000f GP_SAMPLERS_LOG2 */ + dd_emit(ctx, 1, 0); /* 000000ff */ + dd_emit(ctx, 1, 0); /* ffffffff */ + dd_emit(ctx, 1, 4); /* 000000ff UNK12B0_0 */ + dd_emit(ctx, 1, 0x70); /* 000000ff UNK12B0_1 */ + dd_emit(ctx, 1, 0x80); /* 000000ff UNK12B0_3 */ + dd_emit(ctx, 1, 0); /* 000000ff UNK12B0_2 */ + dd_emit(ctx, 1, 0); /* 0000000f FP_TEXTURES_LOG2 */ + dd_emit(ctx, 1, 0); /* 0000000f FP_SAMPLERS_LOG2 */ + if (IS_NVA3F(device->chipset)) { + dd_emit(ctx, 1, 0); /* ffffffff */ + dd_emit(ctx, 1, 0); /* 0000007f MULTISAMPLE_SAMPLES_LOG2 */ + } else { + dd_emit(ctx, 1, 0); /* 0000000f MULTISAMPLE_SAMPLES_LOG2 */ + } + dd_emit(ctx, 1, 0xc); /* 000000ff SEMANTIC_COLOR.BFC0_ID */ + if (device->chipset != 0x50) + dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_COLOR.CLMP_EN */ + dd_emit(ctx, 1, 8); /* 000000ff SEMANTIC_COLOR.COLR_NR */ + dd_emit(ctx, 1, 0x14); /* 000000ff SEMANTIC_COLOR.FFC0_ID */ + if (device->chipset == 0x50) { + dd_emit(ctx, 1, 0); /* 000000ff SEMANTIC_LAYER */ + dd_emit(ctx, 1, 0); /* 00000001 */ + } else { + dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_PTSZ.ENABLE */ + dd_emit(ctx, 1, 0x29); /* 000000ff SEMANTIC_PTSZ.PTSZ_ID */ + dd_emit(ctx, 1, 0x27); /* 000000ff SEMANTIC_PRIM */ + dd_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ + dd_emit(ctx, 1, 8); /* 0000000f SMENATIC_CLIP.CLIP_HIGH */ + dd_emit(ctx, 1, 4); /* 000000ff SEMANTIC_CLIP.CLIP_LO */ + dd_emit(ctx, 1, 0x27); /* 000000ff UNK0FD4 */ + dd_emit(ctx, 1, 0); /* 00000001 UNK1900 */ + } + dd_emit(ctx, 1, 0); /* 00000007 RT_CONTROL_MAP0 */ + dd_emit(ctx, 1, 1); /* 00000007 RT_CONTROL_MAP1 */ + dd_emit(ctx, 1, 2); /* 00000007 RT_CONTROL_MAP2 */ + dd_emit(ctx, 1, 3); /* 00000007 RT_CONTROL_MAP3 */ + dd_emit(ctx, 1, 4); /* 00000007 RT_CONTROL_MAP4 */ + dd_emit(ctx, 1, 5); /* 00000007 RT_CONTROL_MAP5 */ + dd_emit(ctx, 1, 6); /* 00000007 RT_CONTROL_MAP6 */ + dd_emit(ctx, 1, 7); /* 00000007 RT_CONTROL_MAP7 */ + dd_emit(ctx, 1, 1); /* 0000000f RT_CONTROL_COUNT */ + dd_emit(ctx, 8, 0); /* 00000001 RT_HORIZ_UNK */ + dd_emit(ctx, 8, 0); /* ffffffff RT_ADDRESS_LOW */ + dd_emit(ctx, 1, 0xcf); /* 000000ff RT_FORMAT */ + dd_emit(ctx, 7, 0); /* 000000ff RT_FORMAT */ + if (device->chipset != 0x50) + dd_emit(ctx, 3, 0); /* 1, 1, 1 */ + else + dd_emit(ctx, 2, 0); /* 1, 1 */ + dd_emit(ctx, 1, 0); /* ffffffff GP_ENABLE */ + dd_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT*/ + dd_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ + dd_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + if (IS_NVA3F(device->chipset)) { + dd_emit(ctx, 1, 3); /* 00000003 */ + dd_emit(ctx, 1, 0); /* 00000001 UNK1418. Alone. */ + } + if (device->chipset != 0x50) + dd_emit(ctx, 1, 3); /* 00000003 UNK15AC */ + dd_emit(ctx, 1, 1); /* ffffffff RASTERIZE_ENABLE */ + dd_emit(ctx, 1, 0); /* 00000001 FP_CONTROL.EXPORTS_Z */ + if (device->chipset != 0x50) + dd_emit(ctx, 1, 0); /* 00000001 FP_CONTROL.MULTIPLE_RESULTS */ + dd_emit(ctx, 1, 0x12); /* 000000ff FP_INTERPOLANT_CTRL.COUNT */ + dd_emit(ctx, 1, 0x10); /* 000000ff FP_INTERPOLANT_CTRL.COUNT_NONFLAT */ + dd_emit(ctx, 1, 0xc); /* 000000ff FP_INTERPOLANT_CTRL.OFFSET */ + dd_emit(ctx, 1, 1); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.W */ + dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.X */ + dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.Y */ + dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.Z */ + dd_emit(ctx, 1, 4); /* 000000ff FP_RESULT_COUNT */ + dd_emit(ctx, 1, 2); /* ffffffff REG_MODE */ + dd_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */ + if (device->chipset >= 0xa0) + dd_emit(ctx, 1, 0); /* ffffffff */ + dd_emit(ctx, 1, 0); /* 00000001 GP_BUILTIN_RESULT_EN.LAYER_IDX */ + dd_emit(ctx, 1, 0); /* ffffffff STRMOUT_ENABLE */ + dd_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */ + dd_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */ + dd_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE*/ + if (device->chipset != 0x50) + dd_emit(ctx, 8, 0); /* 00000001 */ + if (device->chipset >= 0xa0) { + dd_emit(ctx, 1, 1); /* 00000007 VTX_ATTR_DEFINE.COMP */ + dd_emit(ctx, 1, 1); /* 00000007 VTX_ATTR_DEFINE.SIZE */ + dd_emit(ctx, 1, 2); /* 00000007 VTX_ATTR_DEFINE.TYPE */ + dd_emit(ctx, 1, 0); /* 000000ff VTX_ATTR_DEFINE.ATTR */ + } + dd_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + dd_emit(ctx, 1, 0x14); /* 0000001f ZETA_FORMAT */ + dd_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + dd_emit(ctx, 1, 0); /* 0000000f VP_TEXTURES_LOG2 */ + dd_emit(ctx, 1, 0); /* 0000000f VP_SAMPLERS_LOG2 */ + if (IS_NVA3F(device->chipset)) + dd_emit(ctx, 1, 0); /* 00000001 */ + dd_emit(ctx, 1, 2); /* 00000003 POLYGON_MODE_BACK */ + if (device->chipset >= 0xa0) + dd_emit(ctx, 1, 0); /* 00000003 VTX_ATTR_DEFINE.SIZE - 1 */ + dd_emit(ctx, 1, 0); /* 0000ffff CB_ADDR_INDEX */ + if (device->chipset >= 0xa0) + dd_emit(ctx, 1, 0); /* 00000003 */ + dd_emit(ctx, 1, 0); /* 00000001 CULL_FACE_ENABLE */ + dd_emit(ctx, 1, 1); /* 00000003 CULL_FACE */ + dd_emit(ctx, 1, 0); /* 00000001 FRONT_FACE */ + dd_emit(ctx, 1, 2); /* 00000003 POLYGON_MODE_FRONT */ + dd_emit(ctx, 1, 0x1000); /* 00007fff UNK141C */ + if (device->chipset != 0x50) { + dd_emit(ctx, 1, 0xe00); /* 7fff */ + dd_emit(ctx, 1, 0x1000); /* 7fff */ + dd_emit(ctx, 1, 0x1e00); /* 7fff */ + } + dd_emit(ctx, 1, 0); /* 00000001 BEGIN_END_ACTIVE */ + dd_emit(ctx, 1, 1); /* 00000001 POLYGON_MODE_??? */ + dd_emit(ctx, 1, 1); /* 000000ff GP_REG_ALLOC_TEMP / 4 rounded up */ + dd_emit(ctx, 1, 1); /* 000000ff FP_REG_ALLOC_TEMP... without /4? */ + dd_emit(ctx, 1, 1); /* 000000ff VP_REG_ALLOC_TEMP / 4 rounded up */ + dd_emit(ctx, 1, 1); /* 00000001 */ + dd_emit(ctx, 1, 0); /* 00000001 */ + dd_emit(ctx, 1, 0); /* 00000001 VTX_ATTR_MASK_UNK0 nonempty */ + dd_emit(ctx, 1, 0); /* 00000001 VTX_ATTR_MASK_UNK1 nonempty */ + dd_emit(ctx, 1, 0x200); /* 0003ffff GP_VERTEX_OUTPUT_COUNT*GP_REG_ALLOC_RESULT */ + if (IS_NVA3F(device->chipset)) + dd_emit(ctx, 1, 0x200); + dd_emit(ctx, 1, 0); /* 00000001 */ + if (device->chipset < 0xa0) { + dd_emit(ctx, 1, 1); /* 00000001 */ + dd_emit(ctx, 1, 0x70); /* 000000ff */ + dd_emit(ctx, 1, 0x80); /* 000000ff */ + dd_emit(ctx, 1, 0); /* 000000ff */ + dd_emit(ctx, 1, 0); /* 00000001 */ + dd_emit(ctx, 1, 1); /* 00000001 */ + dd_emit(ctx, 1, 0x70); /* 000000ff */ + dd_emit(ctx, 1, 0x80); /* 000000ff */ + dd_emit(ctx, 1, 0); /* 000000ff */ + } else { + dd_emit(ctx, 1, 1); /* 00000001 */ + dd_emit(ctx, 1, 0xf0); /* 000000ff */ + dd_emit(ctx, 1, 0xff); /* 000000ff */ + dd_emit(ctx, 1, 0); /* 000000ff */ + dd_emit(ctx, 1, 0); /* 00000001 */ + dd_emit(ctx, 1, 1); /* 00000001 */ + dd_emit(ctx, 1, 0xf0); /* 000000ff */ + dd_emit(ctx, 1, 0xff); /* 000000ff */ + dd_emit(ctx, 1, 0); /* 000000ff */ + dd_emit(ctx, 1, 9); /* 0000003f UNK114C.COMP,SIZE */ + } + + /* eng2d state */ + dd_emit(ctx, 1, 0); /* 00000001 eng2d COLOR_KEY_ENABLE */ + dd_emit(ctx, 1, 0); /* 00000007 eng2d COLOR_KEY_FORMAT */ + dd_emit(ctx, 1, 1); /* ffffffff eng2d DST_DEPTH */ + dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d DST_FORMAT */ + dd_emit(ctx, 1, 0); /* ffffffff eng2d DST_LAYER */ + dd_emit(ctx, 1, 1); /* 00000001 eng2d DST_LINEAR */ + dd_emit(ctx, 1, 0); /* 00000007 eng2d PATTERN_COLOR_FORMAT */ + dd_emit(ctx, 1, 0); /* 00000007 eng2d OPERATION */ + dd_emit(ctx, 1, 0); /* 00000003 eng2d PATTERN_SELECT */ + dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d SIFC_FORMAT */ + dd_emit(ctx, 1, 0); /* 00000001 eng2d SIFC_BITMAP_ENABLE */ + dd_emit(ctx, 1, 2); /* 00000003 eng2d SIFC_BITMAP_UNK808 */ + dd_emit(ctx, 1, 0); /* ffffffff eng2d BLIT_DU_DX_FRACT */ + dd_emit(ctx, 1, 1); /* ffffffff eng2d BLIT_DU_DX_INT */ + dd_emit(ctx, 1, 0); /* ffffffff eng2d BLIT_DV_DY_FRACT */ + dd_emit(ctx, 1, 1); /* ffffffff eng2d BLIT_DV_DY_INT */ + dd_emit(ctx, 1, 0); /* 00000001 eng2d BLIT_CONTROL_FILTER */ + dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d DRAW_COLOR_FORMAT */ + dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d SRC_FORMAT */ + dd_emit(ctx, 1, 1); /* 00000001 eng2d SRC_LINEAR #2 */ + + num = ctx->ctxvals_pos - base; + ctx->ctxvals_pos = base; + if (IS_NVA3F(device->chipset)) + cp_ctx(ctx, 0x404800, num); + else + cp_ctx(ctx, 0x405400, num); +} + +/* + * xfer areas. These are a pain. + * + * There are 2 xfer areas: the first one is big and contains all sorts of + * stuff, the second is small and contains some per-TP context. + * + * Each area is split into 8 "strands". The areas, when saved to grctx, + * are made of 8-word blocks. Each block contains a single word from + * each strand. The strands are independent of each other, their + * addresses are unrelated to each other, and data in them is closely + * packed together. The strand layout varies a bit between cards: here + * and there, a single word is thrown out in the middle and the whole + * strand is offset by a bit from corresponding one on another chipset. + * For this reason, addresses of stuff in strands are almost useless. + * Knowing sequence of stuff and size of gaps between them is much more + * useful, and that's how we build the strands in our generator. + * + * NVA0 takes this mess to a whole new level by cutting the old strands + * into a few dozen pieces [known as genes], rearranging them randomly, + * and putting them back together to make new strands. Hopefully these + * genes correspond more or less directly to the same PGRAPH subunits + * as in 400040 register. + * + * The most common value in default context is 0, and when the genes + * are separated by 0's, gene bounduaries are quite speculative... + * some of them can be clearly deduced, others can be guessed, and yet + * others won't be resolved without figuring out the real meaning of + * given ctxval. For the same reason, ending point of each strand + * is unknown. Except for strand 0, which is the longest strand and + * its end corresponds to end of the whole xfer. + * + * An unsolved mystery is the seek instruction: it takes an argument + * in bits 8-18, and that argument is clearly the place in strands to + * seek to... but the offsets don't seem to correspond to offsets as + * seen in grctx. Perhaps there's another, real, not randomly-changing + * addressing in strands, and the xfer insn just happens to skip over + * the unused bits? NV10-NV30 PIPE comes to mind... + * + * As far as I know, there's no way to access the xfer areas directly + * without the help of ctxprog. + */ + +static void +xf_emit(struct nvkm_grctx *ctx, int num, u32 val) { + int i; + if (val && ctx->mode == NVKM_GRCTX_VALS) { + for (i = 0; i < num; i++) + nvkm_wo32(ctx->data, 4 * (ctx->ctxvals_pos + (i << 3)), val); + } + ctx->ctxvals_pos += num << 3; +} + +/* Gene declarations... */ + +static void nv50_gr_construct_gene_dispatch(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_m2mf(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_ccache(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_unk10xx(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_unk14xx(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_zcull(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_clipid(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_unk24xx(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_vfetch(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_eng2d(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_csched(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_unk1cxx(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_strmout(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_unk34xx(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_ropm1(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_ropm2(struct nvkm_grctx *ctx); +static void nv50_gr_construct_gene_ropc(struct nvkm_grctx *ctx); +static void nv50_gr_construct_xfer_tp(struct nvkm_grctx *ctx); + +static void +nv50_gr_construct_xfer1(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i; + int offset; + int size = 0; + u32 units = nvkm_rd32(device, 0x1540); + + offset = (ctx->ctxvals_pos+0x3f)&~0x3f; + ctx->ctxvals_base = offset; + + if (device->chipset < 0xa0) { + /* Strand 0 */ + ctx->ctxvals_pos = offset; + nv50_gr_construct_gene_dispatch(ctx); + nv50_gr_construct_gene_m2mf(ctx); + nv50_gr_construct_gene_unk24xx(ctx); + nv50_gr_construct_gene_clipid(ctx); + nv50_gr_construct_gene_zcull(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 1 */ + ctx->ctxvals_pos = offset + 0x1; + nv50_gr_construct_gene_vfetch(ctx); + nv50_gr_construct_gene_eng2d(ctx); + nv50_gr_construct_gene_csched(ctx); + nv50_gr_construct_gene_ropm1(ctx); + nv50_gr_construct_gene_ropm2(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 2 */ + ctx->ctxvals_pos = offset + 0x2; + nv50_gr_construct_gene_ccache(ctx); + nv50_gr_construct_gene_unk1cxx(ctx); + nv50_gr_construct_gene_strmout(ctx); + nv50_gr_construct_gene_unk14xx(ctx); + nv50_gr_construct_gene_unk10xx(ctx); + nv50_gr_construct_gene_unk34xx(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 3: per-ROP group state */ + ctx->ctxvals_pos = offset + 3; + for (i = 0; i < 6; i++) + if (units & (1 << (i + 16))) + nv50_gr_construct_gene_ropc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strands 4-7: per-TP state */ + for (i = 0; i < 4; i++) { + ctx->ctxvals_pos = offset + 4 + i; + if (units & (1 << (2 * i))) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << (2 * i + 1))) + nv50_gr_construct_xfer_tp(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + } else { + /* Strand 0 */ + ctx->ctxvals_pos = offset; + nv50_gr_construct_gene_dispatch(ctx); + nv50_gr_construct_gene_m2mf(ctx); + nv50_gr_construct_gene_unk34xx(ctx); + nv50_gr_construct_gene_csched(ctx); + nv50_gr_construct_gene_unk1cxx(ctx); + nv50_gr_construct_gene_strmout(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 1 */ + ctx->ctxvals_pos = offset + 1; + nv50_gr_construct_gene_unk10xx(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 2 */ + ctx->ctxvals_pos = offset + 2; + if (device->chipset == 0xa0) + nv50_gr_construct_gene_unk14xx(ctx); + nv50_gr_construct_gene_unk24xx(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 3 */ + ctx->ctxvals_pos = offset + 3; + nv50_gr_construct_gene_vfetch(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 4 */ + ctx->ctxvals_pos = offset + 4; + nv50_gr_construct_gene_ccache(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 5 */ + ctx->ctxvals_pos = offset + 5; + nv50_gr_construct_gene_ropm2(ctx); + nv50_gr_construct_gene_ropm1(ctx); + /* per-ROP context */ + for (i = 0; i < 8; i++) + if (units & (1<<(i+16))) + nv50_gr_construct_gene_ropc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 6 */ + ctx->ctxvals_pos = offset + 6; + nv50_gr_construct_gene_zcull(ctx); + nv50_gr_construct_gene_clipid(ctx); + nv50_gr_construct_gene_eng2d(ctx); + if (units & (1 << 0)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 1)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 2)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 3)) + nv50_gr_construct_xfer_tp(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 7 */ + ctx->ctxvals_pos = offset + 7; + if (device->chipset == 0xa0) { + if (units & (1 << 4)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 5)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 6)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 7)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 8)) + nv50_gr_construct_xfer_tp(ctx); + if (units & (1 << 9)) + nv50_gr_construct_xfer_tp(ctx); + } else { + nv50_gr_construct_gene_unk14xx(ctx); + } + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + + ctx->ctxvals_pos = offset + size * 8; + ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f; + cp_lsr (ctx, offset); + cp_out (ctx, CP_SET_XFER_POINTER); + cp_lsr (ctx, size); + cp_out (ctx, CP_SEEK_1); + cp_out (ctx, CP_XFER_1); + cp_wait(ctx, XFER, BUSY); +} + +/* + * non-trivial demagiced parts of ctx init go here + */ + +static void +nv50_gr_construct_gene_dispatch(struct nvkm_grctx *ctx) +{ + /* start of strand 0 */ + struct nvkm_device *device = ctx->device; + /* SEEK */ + if (device->chipset == 0x50) + xf_emit(ctx, 5, 0); + else if (!IS_NVA3F(device->chipset)) + xf_emit(ctx, 6, 0); + else + xf_emit(ctx, 4, 0); + /* SEEK */ + /* the PGRAPH's internal FIFO */ + if (device->chipset == 0x50) + xf_emit(ctx, 8*3, 0); + else + xf_emit(ctx, 0x100*3, 0); + /* and another bonus slot?!? */ + xf_emit(ctx, 3, 0); + /* and YET ANOTHER bonus slot? */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 3, 0); + /* SEEK */ + /* CTX_SWITCH: caches of gr objects bound to subchannels. 8 values, last used index */ + xf_emit(ctx, 9, 0); + /* SEEK */ + xf_emit(ctx, 9, 0); + /* SEEK */ + xf_emit(ctx, 9, 0); + /* SEEK */ + xf_emit(ctx, 9, 0); + /* SEEK */ + if (device->chipset < 0x90) + xf_emit(ctx, 4, 0); + /* SEEK */ + xf_emit(ctx, 2, 0); + /* SEEK */ + xf_emit(ctx, 6*2, 0); + xf_emit(ctx, 2, 0); + /* SEEK */ + xf_emit(ctx, 2, 0); + /* SEEK */ + xf_emit(ctx, 6*2, 0); + xf_emit(ctx, 2, 0); + /* SEEK */ + if (device->chipset == 0x50) + xf_emit(ctx, 0x1c, 0); + else if (device->chipset < 0xa0) + xf_emit(ctx, 0x1e, 0); + else + xf_emit(ctx, 0x22, 0); + /* SEEK */ + xf_emit(ctx, 0x15, 0); +} + +static void +nv50_gr_construct_gene_m2mf(struct nvkm_grctx *ctx) +{ + /* Strand 0, right after dispatch */ + struct nvkm_device *device = ctx->device; + int smallm2mf = 0; + if (device->chipset < 0x92 || device->chipset == 0x98) + smallm2mf = 1; + /* SEEK */ + xf_emit (ctx, 1, 0); /* DMA_NOTIFY instance >> 4 */ + xf_emit (ctx, 1, 0); /* DMA_BUFFER_IN instance >> 4 */ + xf_emit (ctx, 1, 0); /* DMA_BUFFER_OUT instance >> 4 */ + xf_emit (ctx, 1, 0); /* OFFSET_IN */ + xf_emit (ctx, 1, 0); /* OFFSET_OUT */ + xf_emit (ctx, 1, 0); /* PITCH_IN */ + xf_emit (ctx, 1, 0); /* PITCH_OUT */ + xf_emit (ctx, 1, 0); /* LINE_LENGTH */ + xf_emit (ctx, 1, 0); /* LINE_COUNT */ + xf_emit (ctx, 1, 0x21); /* FORMAT: bits 0-4 INPUT_INC, bits 5-9 OUTPUT_INC */ + xf_emit (ctx, 1, 1); /* LINEAR_IN */ + xf_emit (ctx, 1, 0x2); /* TILING_MODE_IN: bits 0-2 y tiling, bits 3-5 z tiling */ + xf_emit (ctx, 1, 0x100); /* TILING_PITCH_IN */ + xf_emit (ctx, 1, 0x100); /* TILING_HEIGHT_IN */ + xf_emit (ctx, 1, 1); /* TILING_DEPTH_IN */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_IN_Z */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_IN */ + xf_emit (ctx, 1, 1); /* LINEAR_OUT */ + xf_emit (ctx, 1, 0x2); /* TILING_MODE_OUT: bits 0-2 y tiling, bits 3-5 z tiling */ + xf_emit (ctx, 1, 0x100); /* TILING_PITCH_OUT */ + xf_emit (ctx, 1, 0x100); /* TILING_HEIGHT_OUT */ + xf_emit (ctx, 1, 1); /* TILING_DEPTH_OUT */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT_Z */ + xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT */ + xf_emit (ctx, 1, 0); /* OFFSET_IN_HIGH */ + xf_emit (ctx, 1, 0); /* OFFSET_OUT_HIGH */ + /* SEEK */ + if (smallm2mf) + xf_emit(ctx, 0x40, 0); /* 20 * ffffffff, 3ffff */ + else + xf_emit(ctx, 0x100, 0); /* 80 * ffffffff, 3ffff */ + xf_emit(ctx, 4, 0); /* 1f/7f, 0, 1f/7f, 0 [1f for smallm2mf, 7f otherwise] */ + /* SEEK */ + if (smallm2mf) + xf_emit(ctx, 0x400, 0); /* ffffffff */ + else + xf_emit(ctx, 0x800, 0); /* ffffffff */ + xf_emit(ctx, 4, 0); /* ff/1ff, 0, 0, 0 [ff for smallm2mf, 1ff otherwise] */ + /* SEEK */ + xf_emit(ctx, 0x40, 0); /* 20 * bits ffffffff, 3ffff */ + xf_emit(ctx, 0x6, 0); /* 1f, 0, 1f, 0, 1f, 0 */ +} + +static void +nv50_gr_construct_gene_ccache(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + xf_emit(ctx, 2, 0); /* RO */ + xf_emit(ctx, 0x800, 0); /* ffffffff */ + switch (device->chipset) { + case 0x50: + case 0x92: + case 0xa0: + xf_emit(ctx, 0x2b, 0); + break; + case 0x84: + xf_emit(ctx, 0x29, 0); + break; + case 0x94: + case 0x96: + case 0xa3: + xf_emit(ctx, 0x27, 0); + break; + case 0x86: + case 0x98: + case 0xa5: + case 0xa8: + case 0xaa: + case 0xac: + case 0xaf: + xf_emit(ctx, 0x25, 0); + break; + } + /* CB bindings, 0x80 of them. first word is address >> 8, second is + * size >> 4 | valid << 24 */ + xf_emit(ctx, 0x100, 0); /* ffffffff CB_DEF */ + xf_emit(ctx, 1, 0); /* 0000007f CB_ADDR_BUFFER */ + xf_emit(ctx, 1, 0); /* 0 */ + xf_emit(ctx, 0x30, 0); /* ff SET_PROGRAM_CB */ + xf_emit(ctx, 1, 0); /* 3f last SET_PROGRAM_CB */ + xf_emit(ctx, 4, 0); /* RO */ + xf_emit(ctx, 0x100, 0); /* ffffffff */ + xf_emit(ctx, 8, 0); /* 1f, 0, 0, ... */ + xf_emit(ctx, 8, 0); /* ffffffff */ + xf_emit(ctx, 4, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 3 */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_CODE_CB */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_TIC */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_TSC */ + xf_emit(ctx, 1, 0); /* 00000001 LINKED_TSC */ + xf_emit(ctx, 1, 0); /* 000000ff TIC_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff TIC_ADDRESS_LOW */ + xf_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */ + xf_emit(ctx, 1, 0); /* 000000ff TSC_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff TSC_ADDRESS_LOW */ + xf_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */ + xf_emit(ctx, 1, 0); /* 000000ff VP_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff VP_ADDRESS_LOW */ + xf_emit(ctx, 1, 0); /* 00ffffff VP_START_ID */ + xf_emit(ctx, 1, 0); /* 000000ff CB_DEF_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff CB_DEF_ADDRESS_LOW */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0); /* 000000ff GP_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff GP_ADDRESS_LOW */ + xf_emit(ctx, 1, 0); /* 00ffffff GP_START_ID */ + xf_emit(ctx, 1, 0); /* 000000ff FP_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff FP_ADDRESS_LOW */ + xf_emit(ctx, 1, 0); /* 00ffffff FP_START_ID */ +} + +static void +nv50_gr_construct_gene_unk10xx(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i; + /* end of area 2 on pre-NVA0, area 1 on NVAx */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ + xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ + xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ + xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ + if (device->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); + else + xf_emit(ctx, 1, 0x7ff); /* 000007ff */ + xf_emit(ctx, 1, 0); /* 111/113 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + for (i = 0; i < 8; i++) { + switch (device->chipset) { + case 0x50: + case 0x86: + case 0x98: + case 0xaa: + case 0xac: + xf_emit(ctx, 0xa0, 0); /* ffffffff */ + break; + case 0x84: + case 0x92: + case 0x94: + case 0x96: + xf_emit(ctx, 0x120, 0); + break; + case 0xa5: + case 0xa8: + xf_emit(ctx, 0x100, 0); /* ffffffff */ + break; + case 0xa0: + case 0xa3: + case 0xaf: + xf_emit(ctx, 0x400, 0); /* ffffffff */ + break; + } + xf_emit(ctx, 4, 0); /* 3f, 0, 0, 0 */ + xf_emit(ctx, 4, 0); /* ffffffff */ + } + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ + xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_TEMP */ + xf_emit(ctx, 1, 1); /* 00000001 RASTERIZE_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 0x27); /* 000000ff UNK0FD4 */ + xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ + xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ +} + +static void +nv50_gr_construct_gene_unk34xx(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + /* end of area 2 on pre-NVA0, area 1 on NVAx */ + xf_emit(ctx, 1, 0); /* 00000001 VIEWPORT_CLIP_RECTS_EN */ + xf_emit(ctx, 1, 0); /* 00000003 VIEWPORT_CLIP_MODE */ + xf_emit(ctx, 0x10, 0x04000000); /* 07ffffff VIEWPORT_CLIP_HORIZ*8, VIEWPORT_CLIP_VERT*8 */ + xf_emit(ctx, 1, 0); /* 00000001 POLYGON_STIPPLE_ENABLE */ + xf_emit(ctx, 0x20, 0); /* ffffffff POLYGON_STIPPLE */ + xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0D64 */ + xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0DF4 */ + xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0x1fe21); /* 0001ffff tesla UNK0FAC */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 1, 0x0fac6881); + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 1); + xf_emit(ctx, 3, 0); + } +} + +static void +nv50_gr_construct_gene_unk14xx(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + /* middle of area 2 on pre-NVA0, beginning of area 2 on NVA0, area 7 on >NVA0 */ + if (device->chipset != 0x50) { + xf_emit(ctx, 5, 0); /* ffffffff */ + xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + xf_emit(ctx, 1, 0x804); /* 00000fff SEMANTIC_CLIP */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 2, 4); /* 7f, ff */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + } + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 1, 0); /* 000000ff VP_CLIP_DISTANCE_ENABLE */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 0); /* 3ff */ + xf_emit(ctx, 1, 0); /* 000000ff tesla UNK1940 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0D7C */ + xf_emit(ctx, 1, 0x804); /* 00000fff SEMANTIC_CLIP */ + xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */ + xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 0x7f); /* 000000ff tesla UNK0FFC */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 1); /* 00000001 SHADE_MODEL */ + xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0D7C */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0F8C */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 4, 0); /* ffffffff NOPERSPECTIVE_BITMAP */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 0); /* 0000000f */ + if (device->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); /* 000003ff tesla UNK0D68 */ + else + xf_emit(ctx, 1, 0x7ff); /* 000007ff tesla UNK0D68 */ + xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ + xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ + xf_emit(ctx, 0x30, 0); /* ffffffff VIEWPORT_SCALE: X0, Y0, Z0, X1, Y1, ... */ + xf_emit(ctx, 3, 0); /* f, 0, 0 */ + xf_emit(ctx, 3, 0); /* ffffffff last VIEWPORT_SCALE? */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */ + xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 0x30, 0); /* ffffffff VIEWPORT_TRANSLATE */ + xf_emit(ctx, 3, 0); /* f, 0, 0 */ + xf_emit(ctx, 3, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 2, 0x88); /* 000001ff tesla UNK19D8 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */ + xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */ + xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ + xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ + xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */ + xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ + xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 0); /* 0000000f */ + xf_emit(ctx, 1, 0x3f800000); /* ffffffff LINE_WIDTH */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */ + if (device->chipset != 0x50) { + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + } + xf_emit(ctx, 0x20, 0); /* 10xbits ffffffff, 3fffff. SCISSOR_* */ + xf_emit(ctx, 1, 0); /* f */ + xf_emit(ctx, 1, 0); /* 0? */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 003fffff */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 0x52); /* 000001ff SEMANTIC_PTSZ */ + xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ + xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ + xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ + xf_emit(ctx, 1, 0); /* 0000000f */ +} + +static void +nv50_gr_construct_gene_zcull(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + /* end of strand 0 on pre-NVA0, beginning of strand 6 on NVAx */ + /* SEEK */ + xf_emit(ctx, 1, 0x3f); /* 0000003f UNK1590 */ + xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ + xf_emit(ctx, 2, 0x04000000); /* 07ffffff tesla UNK0D6C */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 0); /* 00000001 CLIPID_ENABLE */ + xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */ + xf_emit(ctx, 1, 0); /* 0000ffff */ + xf_emit(ctx, 1, 0); /* 00000001 UNK0FB0 */ + xf_emit(ctx, 1, 0); /* 00000001 POLYGON_STIPPLE_ENABLE */ + xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ + xf_emit(ctx, 1, 0); /* 000000ff CLEAR_STENCIL */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */ + xf_emit(ctx, 1, 0); /* 00000007 */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1108 */ + xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0x1001); /* 00001fff ZETA_ARRAY_MODE */ + /* SEEK */ + xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ + xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */ + xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */ + xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */ + xf_emit(ctx, 1, 0x10); /* 7f/ff/3ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 1, 0); /* 00000001 VIEWPORT_CLIP_RECTS_EN */ + xf_emit(ctx, 1, 3); /* 00000003 FP_CTRL_UNK196C */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1968 */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 0); /* 0fffffff tesla UNK1104 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK151C */ +} + +static void +nv50_gr_construct_gene_clipid(struct nvkm_grctx *ctx) +{ + /* middle of strand 0 on pre-NVA0 [after 24xx], middle of area 6 on NVAx */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* 00000007 UNK0FB4 */ + /* SEEK */ + xf_emit(ctx, 4, 0); /* 07ffffff CLIPID_REGION_HORIZ */ + xf_emit(ctx, 4, 0); /* 07ffffff CLIPID_REGION_VERT */ + xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */ + xf_emit(ctx, 2, 0x04000000); /* 07ffffff UNK1508 */ + xf_emit(ctx, 1, 0); /* 00000001 CLIPID_ENABLE */ + xf_emit(ctx, 1, 0x80); /* 00003fff CLIPID_WIDTH */ + xf_emit(ctx, 1, 0); /* 000000ff CLIPID_ID */ + xf_emit(ctx, 1, 0); /* 000000ff CLIPID_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff CLIPID_ADDRESS_LOW */ + xf_emit(ctx, 1, 0x80); /* 00003fff CLIPID_HEIGHT */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_CLIPID */ +} + +static void +nv50_gr_construct_gene_unk24xx(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i; + /* middle of strand 0 on pre-NVA0 [after m2mf], end of strand 2 on NVAx */ + /* SEEK */ + xf_emit(ctx, 0x33, 0); + /* SEEK */ + xf_emit(ctx, 2, 0); + /* SEEK */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + /* SEEK */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 4, 0); /* RO */ + xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ + xf_emit(ctx, 1, 0); /* 1ff */ + xf_emit(ctx, 8, 0); /* 0? */ + xf_emit(ctx, 9, 0); /* ffffffff, 7ff */ + + xf_emit(ctx, 4, 0); /* RO */ + xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ + xf_emit(ctx, 1, 0); /* 1ff */ + xf_emit(ctx, 8, 0); /* 0? */ + xf_emit(ctx, 9, 0); /* ffffffff, 7ff */ + } else { + xf_emit(ctx, 0xc, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ + xf_emit(ctx, 1, 0); /* 1ff */ + xf_emit(ctx, 8, 0); /* 0? */ + + /* SEEK */ + xf_emit(ctx, 0xc, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ + xf_emit(ctx, 1, 0); /* 1ff */ + xf_emit(ctx, 8, 0); /* 0? */ + } + /* SEEK */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ + xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ + xf_emit(ctx, 1, 1); /* 00000001 */ + /* SEEK */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 2, 4); /* 000000ff */ + xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ + xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 1, 0x27); /* 000000ff SEMANTIC_PRIM_ID */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000000f */ + xf_emit(ctx, 1, 1); /* 00000001 */ + for (i = 0; i < 10; i++) { + /* SEEK */ + xf_emit(ctx, 0x40, 0); /* ffffffff */ + xf_emit(ctx, 0x10, 0); /* 3, 0, 0.... */ + xf_emit(ctx, 0x10, 0); /* ffffffff */ + } + /* SEEK */ + xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_CTRL */ + xf_emit(ctx, 1, 1); /* 00000001 */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 4, 0); /* ffffffff NOPERSPECTIVE_BITMAP */ + xf_emit(ctx, 0x10, 0); /* 00ffffff POINT_COORD_REPLACE_MAP */ + xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 0); /* 000003ff */ +} + +static void +nv50_gr_construct_gene_vfetch(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int acnt = 0x10, rep, i; + /* beginning of strand 1 on pre-NVA0, strand 3 on NVAx */ + if (IS_NVA3F(device->chipset)) + acnt = 0x20; + /* SEEK */ + if (device->chipset >= 0xa0) { + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK13A4 */ + xf_emit(ctx, 1, 1); /* 00000fff tesla UNK1318 */ + } + xf_emit(ctx, 1, 0); /* ffffffff VERTEX_BUFFER_FIRST */ + xf_emit(ctx, 1, 0); /* 00000001 PRIMITIVE_RESTART_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 UNK0DE8 */ + xf_emit(ctx, 1, 0); /* ffffffff PRIMITIVE_RESTART_INDEX */ + xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, acnt/8, 0); /* ffffffff VTX_ATR_MASK_UNK0DD0 */ + xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ + xf_emit(ctx, 1, 0x20); /* 0000ffff tesla UNK129C */ + xf_emit(ctx, 1, 0); /* 000000ff turing UNK370??? */ + xf_emit(ctx, 1, 0); /* 0000ffff turing USER_PARAM_COUNT */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + /* SEEK */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 0xb, 0); /* RO */ + else if (device->chipset >= 0xa0) + xf_emit(ctx, 0x9, 0); /* RO */ + else + xf_emit(ctx, 0x8, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* 00000001 EDGE_FLAG */ + xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + /* SEEK */ + xf_emit(ctx, 0xc, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* 7f/ff */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ + xf_emit(ctx, 1, 4); /* 000001ff UNK1A28 */ + xf_emit(ctx, 1, 8); /* 000001ff UNK0DF0 */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + if (device->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); /* 3ff tesla UNK0D68 */ + else + xf_emit(ctx, 1, 0x7ff); /* 7ff tesla UNK0D68 */ + if (device->chipset == 0xa8) + xf_emit(ctx, 1, 0x1e00); /* 7fff */ + /* SEEK */ + xf_emit(ctx, 0xc, 0); /* RO or close */ + /* SEEK */ + xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ + if (device->chipset > 0x50 && device->chipset < 0xa0) + xf_emit(ctx, 2, 0); /* ffffffff */ + else + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0FD8 */ + /* SEEK */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 0x10, 0); /* 0? */ + xf_emit(ctx, 2, 0); /* weird... */ + xf_emit(ctx, 2, 0); /* RO */ + } else { + xf_emit(ctx, 8, 0); /* 0? */ + xf_emit(ctx, 1, 0); /* weird... */ + xf_emit(ctx, 2, 0); /* RO */ + } + /* SEEK */ + xf_emit(ctx, 1, 0); /* ffffffff VB_ELEMENT_BASE */ + xf_emit(ctx, 1, 0); /* ffffffff UNK1438 */ + xf_emit(ctx, acnt, 0); /* 1 tesla UNK1000 */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1118? */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_UNK90C */ + xf_emit(ctx, 1, 0); /* f/1f */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_UNK90C */ + xf_emit(ctx, 1, 0); /* f/1f */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* RO */ + xf_emit(ctx, 2, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK111C? */ + xf_emit(ctx, 1, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* 000000ff UNK15F4_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff UNK15F4_ADDRESS_LOW */ + xf_emit(ctx, 1, 0); /* 000000ff UNK0F84_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff UNK0F84_ADDRESS_LOW */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* 00003fff VERTEX_ARRAY_ATTRIB_OFFSET */ + xf_emit(ctx, 3, 0); /* f/1f */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* 00000fff VERTEX_ARRAY_STRIDE */ + xf_emit(ctx, 3, 0); /* f/1f */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_LOW */ + xf_emit(ctx, 3, 0); /* f/1f */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* 000000ff VERTEX_ARRAY_HIGH */ + xf_emit(ctx, 3, 0); /* f/1f */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_LIMIT_LOW */ + xf_emit(ctx, 3, 0); /* f/1f */ + /* SEEK */ + xf_emit(ctx, acnt, 0); /* 000000ff VERTEX_LIMIT_HIGH */ + xf_emit(ctx, 3, 0); /* f/1f */ + /* SEEK */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, acnt, 0); /* f */ + xf_emit(ctx, 3, 0); /* f/1f */ + } + /* SEEK */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 2, 0); /* RO */ + else + xf_emit(ctx, 5, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* ffff DMA_VTXBUF */ + /* SEEK */ + if (device->chipset < 0xa0) { + xf_emit(ctx, 0x41, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 0x11, 0); /* RO */ + } else if (!IS_NVA3F(device->chipset)) + xf_emit(ctx, 0x50, 0); /* RO */ + else + xf_emit(ctx, 0x58, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, 1, 1); /* 1 UNK0DEC */ + /* SEEK */ + xf_emit(ctx, acnt*4, 0); /* ffffffff VTX_ATTR */ + xf_emit(ctx, 4, 0); /* f/1f, 0, 0, 0 */ + /* SEEK */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 0x1d, 0); /* RO */ + else + xf_emit(ctx, 0x16, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ + /* SEEK */ + if (device->chipset < 0xa0) + xf_emit(ctx, 8, 0); /* RO */ + else if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 0xc, 0); /* RO */ + else + xf_emit(ctx, 7, 0); /* RO */ + /* SEEK */ + xf_emit(ctx, 0xa, 0); /* RO */ + if (device->chipset == 0xa0) + rep = 0xc; + else + rep = 4; + for (i = 0; i < rep; i++) { + /* SEEK */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 0x20, 0); /* ffffffff */ + xf_emit(ctx, 0x200, 0); /* ffffffff */ + xf_emit(ctx, 4, 0); /* 7f/ff, 0, 0, 0 */ + xf_emit(ctx, 4, 0); /* ffffffff */ + } + /* SEEK */ + xf_emit(ctx, 1, 0); /* 113/111 */ + xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ + xf_emit(ctx, acnt/8, 0); /* ffffffff VTX_ATTR_MASK_UNK0DD0 */ + xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + /* SEEK */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 7, 0); /* weird... */ + else + xf_emit(ctx, 5, 0); /* weird... */ +} + +static void +nv50_gr_construct_gene_eng2d(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + /* middle of strand 1 on pre-NVA0 [after vfetch], middle of strand 6 on NVAx */ + /* SEEK */ + xf_emit(ctx, 2, 0); /* 0001ffff CLIP_X, CLIP_Y */ + xf_emit(ctx, 2, 0); /* 0000ffff CLIP_W, CLIP_H */ + xf_emit(ctx, 1, 0); /* 00000001 CLIP_ENABLE */ + if (device->chipset < 0xa0) { + /* this is useless on everything but the original NV50, + * guess they forgot to nuke it. Or just didn't bother. */ + xf_emit(ctx, 2, 0); /* 0000ffff IFC_CLIP_X, Y */ + xf_emit(ctx, 2, 1); /* 0000ffff IFC_CLIP_W, H */ + xf_emit(ctx, 1, 0); /* 00000001 IFC_CLIP_ENABLE */ + } + xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ + xf_emit(ctx, 1, 0x100); /* 0001ffff DST_WIDTH */ + xf_emit(ctx, 1, 0x100); /* 0001ffff DST_HEIGHT */ + xf_emit(ctx, 1, 0x11); /* 3f[NV50]/7f[NV84+] DST_FORMAT */ + xf_emit(ctx, 1, 0); /* 0001ffff DRAW_POINT_X */ + xf_emit(ctx, 1, 8); /* 0000000f DRAW_UNK58C */ + xf_emit(ctx, 1, 0); /* 000fffff SIFC_DST_X_FRACT */ + xf_emit(ctx, 1, 0); /* 0001ffff SIFC_DST_X_INT */ + xf_emit(ctx, 1, 0); /* 000fffff SIFC_DST_Y_FRACT */ + xf_emit(ctx, 1, 0); /* 0001ffff SIFC_DST_Y_INT */ + xf_emit(ctx, 1, 0); /* 000fffff SIFC_DX_DU_FRACT */ + xf_emit(ctx, 1, 1); /* 0001ffff SIFC_DX_DU_INT */ + xf_emit(ctx, 1, 0); /* 000fffff SIFC_DY_DV_FRACT */ + xf_emit(ctx, 1, 1); /* 0001ffff SIFC_DY_DV_INT */ + xf_emit(ctx, 1, 1); /* 0000ffff SIFC_WIDTH */ + xf_emit(ctx, 1, 1); /* 0000ffff SIFC_HEIGHT */ + xf_emit(ctx, 1, 0xcf); /* 000000ff SIFC_FORMAT */ + xf_emit(ctx, 1, 2); /* 00000003 SIFC_BITMAP_UNK808 */ + xf_emit(ctx, 1, 0); /* 00000003 SIFC_BITMAP_LINE_PACK_MODE */ + xf_emit(ctx, 1, 0); /* 00000001 SIFC_BITMAP_LSB_FIRST */ + xf_emit(ctx, 1, 0); /* 00000001 SIFC_BITMAP_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000ffff BLIT_DST_X */ + xf_emit(ctx, 1, 0); /* 0000ffff BLIT_DST_Y */ + xf_emit(ctx, 1, 0); /* 000fffff BLIT_DU_DX_FRACT */ + xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DU_DX_INT */ + xf_emit(ctx, 1, 0); /* 000fffff BLIT_DV_DY_FRACT */ + xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DV_DY_INT */ + xf_emit(ctx, 1, 1); /* 0000ffff BLIT_DST_W */ + xf_emit(ctx, 1, 1); /* 0000ffff BLIT_DST_H */ + xf_emit(ctx, 1, 0); /* 000fffff BLIT_SRC_X_FRACT */ + xf_emit(ctx, 1, 0); /* 0001ffff BLIT_SRC_X_INT */ + xf_emit(ctx, 1, 0); /* 000fffff BLIT_SRC_Y_FRACT */ + xf_emit(ctx, 1, 0); /* 00000001 UNK888 */ + xf_emit(ctx, 1, 4); /* 0000003f UNK884 */ + xf_emit(ctx, 1, 0); /* 00000007 UNK880 */ + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK0FB8 */ + xf_emit(ctx, 1, 0x15); /* 000000ff tesla UNK128C */ + xf_emit(ctx, 2, 0); /* 00000007, ffff0ff3 */ + xf_emit(ctx, 1, 0); /* 00000001 UNK260 */ + xf_emit(ctx, 1, 0x4444480); /* 1fffffff UNK870 */ + /* SEEK */ + xf_emit(ctx, 0x10, 0); + /* SEEK */ + xf_emit(ctx, 0x27, 0); +} + +static void +nv50_gr_construct_gene_csched(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + /* middle of strand 1 on pre-NVA0 [after eng2d], middle of strand 0 on NVAx */ + /* SEEK */ + xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY... what is it doing here??? */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */ + xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 1, 0); /* 000003ff */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* ffffffff turing UNK364 */ + xf_emit(ctx, 1, 0); /* 0000000f turing UNK36C */ + xf_emit(ctx, 1, 0); /* 0000ffff USER_PARAM_COUNT */ + xf_emit(ctx, 1, 0x100); /* 00ffffff turing UNK384 */ + xf_emit(ctx, 1, 0); /* 0000000f turing UNK2A0 */ + xf_emit(ctx, 1, 0); /* 0000ffff GRIDID */ + xf_emit(ctx, 1, 0x10001); /* ffffffff GRIDDIM_XY */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0x10001); /* ffffffff BLOCKDIM_XY */ + xf_emit(ctx, 1, 1); /* 0000ffff BLOCKDIM_Z */ + xf_emit(ctx, 1, 0x10001); /* 00ffffff BLOCK_ALLOC */ + xf_emit(ctx, 1, 1); /* 00000001 LANES32 */ + xf_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */ + xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */ + /* SEEK */ + xf_emit(ctx, 0x40, 0); /* ffffffff USER_PARAM */ + switch (device->chipset) { + case 0x50: + case 0x92: + xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ + xf_emit(ctx, 0x80, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 0x10*2, 0); /* ffffffff, 1f */ + break; + case 0x84: + xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ + xf_emit(ctx, 0x60, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 0xc*2, 0); /* ffffffff, 1f */ + break; + case 0x94: + case 0x96: + xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ + xf_emit(ctx, 0x40, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 8*2, 0); /* ffffffff, 1f */ + break; + case 0x86: + case 0x98: + xf_emit(ctx, 4, 0); /* f, 0, 0, 0 */ + xf_emit(ctx, 0x10, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 2*2, 0); /* ffffffff, 1f */ + break; + case 0xa0: + xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ + xf_emit(ctx, 0xf0, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 0x1e*2, 0); /* ffffffff, 1f */ + break; + case 0xa3: + xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ + xf_emit(ctx, 0x60, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 0xc*2, 0); /* ffffffff, 1f */ + break; + case 0xa5: + case 0xaf: + xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ + xf_emit(ctx, 0x30, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 6*2, 0); /* ffffffff, 1f */ + break; + case 0xaa: + xf_emit(ctx, 0x12, 0); + break; + case 0xa8: + case 0xac: + xf_emit(ctx, 4, 0); /* f, 0, 0, 0 */ + xf_emit(ctx, 0x10, 0); /* fff */ + xf_emit(ctx, 2, 0); /* ff, fff */ + xf_emit(ctx, 2*2, 0); /* ffffffff, 1f */ + break; + } + xf_emit(ctx, 1, 0); /* 0000000f */ + xf_emit(ctx, 1, 0); /* 00000000 */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 0000001f */ + xf_emit(ctx, 4, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 00000003 turing UNK35C */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 4, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 00000003 turing UNK35C */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 000000ff */ +} + +static void +nv50_gr_construct_gene_unk1cxx(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ + xf_emit(ctx, 1, 0x3f800000); /* ffffffff LINE_WIDTH */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1658 */ + xf_emit(ctx, 1, 0); /* 00000001 POLYGON_SMOOTH_ENABLE */ + xf_emit(ctx, 3, 0); /* 00000001 POLYGON_OFFSET_*_ENABLE */ + xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */ + xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK165C */ + xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ + xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ + xf_emit(ctx, 1, 0); /* ffffffff POLYGON_OFFSET_UNITS */ + xf_emit(ctx, 1, 0); /* ffffffff POLYGON_OFFSET_FACTOR */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1668 */ + xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 1, 0x11); /* 0000007f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 0000007f RT_FORMAT */ + xf_emit(ctx, 8, 0); /* 00000001 RT_HORIZ_LINEAR */ + xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 3); /* 00000003 UNK16B4 */ + else if (device->chipset >= 0xa0) + xf_emit(ctx, 1, 1); /* 00000001 UNK16B4 */ + xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0F90 */ + xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ + xf_emit(ctx, 2, 0x04000000); /* 07ffffff tesla UNK0D6C */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ + xf_emit(ctx, 1, 5); /* 0000000f UNK1408 */ + xf_emit(ctx, 1, 0x52); /* 000001ff SEMANTIC_PTSZ */ + xf_emit(ctx, 1, 0); /* ffffffff POINT_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 00000007 tesla UNK0FB4 */ + if (device->chipset != 0x50) { + xf_emit(ctx, 1, 0); /* 3ff */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK1110 */ + } + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1928 */ + xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */ + xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */ + xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 0x20, 0); /* 07ffffff VIEWPORT_HORIZ, then VIEWPORT_VERT. (W&0x3fff)<<13 | (X&0x1fff). */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK187C */ + xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ + xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 1, 5); /* 0000000f tesla UNK1220 */ + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 000000ff tesla UNK1A20 */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ + xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */ + if (device->chipset < 0xa0) + xf_emit(ctx, 0x1c, 0); /* RO */ + else if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 0x9, 0); + xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ + xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ + xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ + if (device->chipset != 0x50) { + xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */ + xf_emit(ctx, 1, 0); /* 3ff */ + } + /* XXX: the following block could belong either to unk1cxx, or + * to STRMOUT. Rather hard to tell. */ + if (device->chipset < 0xa0) + xf_emit(ctx, 0x25, 0); + else + xf_emit(ctx, 0x3b, 0); +} + +static void +nv50_gr_construct_gene_strmout(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + xf_emit(ctx, 1, 0x102); /* 0000ffff STRMOUT_BUFFER_CTRL */ + xf_emit(ctx, 1, 0); /* ffffffff STRMOUT_PRIMITIVE_COUNT */ + xf_emit(ctx, 4, 4); /* 000000ff STRMOUT_NUM_ATTRIBS */ + if (device->chipset >= 0xa0) { + xf_emit(ctx, 4, 0); /* ffffffff UNK1A8C */ + xf_emit(ctx, 4, 0); /* ffffffff UNK1780 */ + } + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + if (device->chipset == 0x50) + xf_emit(ctx, 1, 0x3ff); /* 000003ff tesla UNK0D68 */ + else + xf_emit(ctx, 1, 0x7ff); /* 000007ff tesla UNK0D68 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + /* SEEK */ + xf_emit(ctx, 1, 0x102); /* 0000ffff STRMOUT_BUFFER_CTRL */ + xf_emit(ctx, 1, 0); /* ffffffff STRMOUT_PRIMITIVE_COUNT */ + xf_emit(ctx, 4, 0); /* 000000ff STRMOUT_ADDRESS_HIGH */ + xf_emit(ctx, 4, 0); /* ffffffff STRMOUT_ADDRESS_LOW */ + xf_emit(ctx, 4, 4); /* 000000ff STRMOUT_NUM_ATTRIBS */ + if (device->chipset >= 0xa0) { + xf_emit(ctx, 4, 0); /* ffffffff UNK1A8C */ + xf_emit(ctx, 4, 0); /* ffffffff UNK1780 */ + } + xf_emit(ctx, 1, 0); /* 0000ffff DMA_STRMOUT */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */ + xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */ + xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW QUERY_COUNTER */ + xf_emit(ctx, 2, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + /* SEEK */ + xf_emit(ctx, 0x20, 0); /* ffffffff STRMOUT_MAP */ + xf_emit(ctx, 1, 0); /* 0000000f */ + xf_emit(ctx, 1, 0); /* 00000000? */ + xf_emit(ctx, 2, 0); /* ffffffff */ +} + +static void +nv50_gr_construct_gene_ropm1(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0D64 */ + xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0DF4 */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 0x11); /* 000000ff tesla UNK1968 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ +} + +static void +nv50_gr_construct_gene_ropm2(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + /* SEEK */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 2, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */ + xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW, COUNTER */ + xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ + xf_emit(ctx, 1, 0); /* 7 */ + /* SEEK */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */ + xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */ + xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW, COUNTER */ + xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0D64 */ + xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0DF4 */ + xf_emit(ctx, 1, 0); /* 00000001 eng2d UNK260 */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 0); /* 00000007 */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 0x11); /* 000000ff tesla UNK1968 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ +} + +static void +nv50_gr_construct_gene_ropc(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int magic2; + if (device->chipset == 0x50) { + magic2 = 0x00003e60; + } else if (!IS_NVA3F(device->chipset)) { + magic2 = 0x001ffe67; + } else { + magic2 = 0x00087e67; + } + xf_emit(ctx, 1, 0); /* f/7 MUTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + if (device->chipset >= 0xa0 && !IS_NVAAF(device->chipset)) + xf_emit(ctx, 1, 0x15); /* 000000ff */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ + xf_emit(ctx, 1, 0x10); /* 3ff/ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + if (device->chipset == 0x86 || device->chipset == 0x92 || device->chipset == 0x98 || device->chipset >= 0xa0) { + xf_emit(ctx, 3, 0); /* ff, ffffffff, ffffffff */ + xf_emit(ctx, 1, 4); /* 7 */ + xf_emit(ctx, 1, 0x400); /* fffffff */ + xf_emit(ctx, 1, 0x300); /* ffff */ + xf_emit(ctx, 1, 0x1001); /* 1fff */ + if (device->chipset != 0xa0) { + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 0); /* 0000000f UNK15C8 */ + else + xf_emit(ctx, 1, 0x15); /* ff */ + } + } + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ + xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */ + xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000000f */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ + xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */ + xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 0); /* 000000ff CLEAR_STENCIL */ + xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ + xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 0x3f); /* 0000003f UNK1590 */ + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 2, 0); /* ffff0ff3, ffff */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */ + xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */ + if (device->chipset >= 0xa0) { + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x1001); + xf_emit(ctx, 0xb, 0); + } else { + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + } + xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 1, 0x11); /* 3f/7f */ + xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ + if (device->chipset != 0x50) { + xf_emit(ctx, 1, 0); /* 0000000f LOGIC_OP */ + xf_emit(ctx, 1, 0); /* 000000ff */ + } + xf_emit(ctx, 1, 0); /* 00000007 OPERATION */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ + xf_emit(ctx, 2, 1); /* 00000007 BLEND_EQUATION_RGB, ALPHA */ + xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK12E4 */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ + xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1140 */ + xf_emit(ctx, 2, 0); /* 00000001 */ + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, 0); /* 0000000f */ + xf_emit(ctx, 1, 0); /* 00000003 */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 2, 0); /* 00000001 */ + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + } else if (device->chipset >= 0xa0) { + xf_emit(ctx, 2, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0); /* 00000003 */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 2, 0); /* 00000001 */ + } else { + xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1430 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + } + xf_emit(ctx, 4, 0); /* ffffffff CLEAR_COLOR */ + xf_emit(ctx, 4, 0); /* ffffffff BLEND_COLOR A R G B */ + xf_emit(ctx, 1, 0); /* 00000fff eng2d UNK2B0 */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 2, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ + xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ + xf_emit(ctx, 1, 0); /* 00000001 UNK19C0 */ + xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000000f LOGIC_OP */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 1, 0); /* 00000001 UNK12E4? NVA3+ only? */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK15C4 */ + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1140 */ + } + xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ + xf_emit(ctx, 1, 0); /* 00000007 PATTERN_COLOR_FORMAT */ + xf_emit(ctx, 2, 0); /* ffffffff PATTERN_MONO_COLOR */ + xf_emit(ctx, 1, 0); /* 00000001 PATTERN_MONO_FORMAT */ + xf_emit(ctx, 2, 0); /* ffffffff PATTERN_MONO_BITMAP */ + xf_emit(ctx, 1, 0); /* 00000003 PATTERN_SELECT */ + xf_emit(ctx, 1, 0); /* 000000ff ROP */ + xf_emit(ctx, 1, 0); /* ffffffff BETA1 */ + xf_emit(ctx, 1, 0); /* ffffffff BETA4 */ + xf_emit(ctx, 1, 0); /* 00000007 OPERATION */ + xf_emit(ctx, 0x50, 0); /* 10x ffffff, ffffff, ffffff, ffffff, 3 PATTERN */ +} + +static void +nv50_gr_construct_xfer_unk84xx(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int magic3; + switch (device->chipset) { + case 0x50: + magic3 = 0x1000; + break; + case 0x86: + case 0x98: + case 0xa8: + case 0xaa: + case 0xac: + case 0xaf: + magic3 = 0x1e00; + break; + default: + magic3 = 0; + } + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 7f/ff[NVA0+] VP_REG_ALLOC_RESULT */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 0); /* 111/113[NVA0+] */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 0x1f, 0); /* ffffffff */ + else if (device->chipset >= 0xa0) + xf_emit(ctx, 0x0f, 0); /* ffffffff */ + else + xf_emit(ctx, 0x10, 0); /* fffffff VP_RESULT_MAP_1 up */ + xf_emit(ctx, 2, 0); /* f/1f[NVA3], fffffff/ffffffff[NVA0+] */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 1, 0x03020100); /* ffffffff */ + else + xf_emit(ctx, 1, 0x00608080); /* fffffff VP_RESULT_MAP_0 */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 2, 0); /* 111/113, 7f/ff */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ + if (magic3) + xf_emit(ctx, 1, magic3); /* 00007fff tesla UNK141C */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 0); /* 111/113 */ + xf_emit(ctx, 0x1f, 0); /* ffffffff GP_RESULT_MAP_1 up */ + xf_emit(ctx, 1, 0); /* 0000001f */ + xf_emit(ctx, 1, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ + xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0x03020100); /* ffffffff GP_RESULT_MAP_0 */ + xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */ + if (magic3) + xf_emit(ctx, 1, magic3); /* 7fff tesla UNK141C */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 0); /* 111/113 */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */ + xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK13A0 */ + xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + xf_emit(ctx, 1, 0); /* 111/113 */ + if (device->chipset == 0x94 || device->chipset == 0x96) + xf_emit(ctx, 0x1020, 0); /* 4 x (0x400 x 0xffffffff, ff, 0, 0, 0, 4 x ffffffff) */ + else if (device->chipset < 0xa0) + xf_emit(ctx, 0xa20, 0); /* 4 x (0x280 x 0xffffffff, ff, 0, 0, 0, 4 x ffffffff) */ + else if (!IS_NVA3F(device->chipset)) + xf_emit(ctx, 0x210, 0); /* ffffffff */ + else + xf_emit(ctx, 0x410, 0); /* ffffffff */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ + xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */ + xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ +} + +static void +nv50_gr_construct_xfer_tprop(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int magic1, magic2; + if (device->chipset == 0x50) { + magic1 = 0x3ff; + magic2 = 0x00003e60; + } else if (!IS_NVA3F(device->chipset)) { + magic1 = 0x7ff; + magic2 = 0x001ffe67; + } else { + magic1 = 0x7ff; + magic2 = 0x00087e67; + } + xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* ffffffff ALPHA_TEST_REF */ + xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000000f UNK16A0 */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 4, 0); /* ffffffff BLEND_COLOR */ + xf_emit(ctx, 1, 0); /* 00000001 UNK19C0 */ + xf_emit(ctx, 1, 0); /* 00000001 UNK0FDC */ + xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ + xf_emit(ctx, 1, 0); /* ff[NV50]/3ff[NV84+] */ + xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ + xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ + xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ + xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */ + xf_emit(ctx, 1, 0); /* 7 */ + xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff COLOR_KEY */ + xf_emit(ctx, 1, 0); /* 00000001 COLOR_KEY_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 COLOR_KEY_FORMAT */ + xf_emit(ctx, 2, 0); /* ffffffff SIFC_BITMAP_COLOR */ + xf_emit(ctx, 1, 1); /* 00000001 SIFC_BITMAP_WRITE_BIT0_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 3); /* 00000003 tesla UNK16B4 */ + xf_emit(ctx, 1, 0); /* 00000003 */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1298 */ + } else if (device->chipset >= 0xa0) { + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK16B4 */ + xf_emit(ctx, 1, 0); /* 00000003 */ + } else { + xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */ + } + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 0); /* 00000001 UNK12E4 */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ + xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_SRC_RGB */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_DST_RGB */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_SRC_ALPHA */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_DST_ALPHA */ + xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ + } + xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ + xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */ + xf_emit(ctx, 1, 0); /* 7 */ + xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ + xf_emit(ctx, 1, 0); /* 00000007 OPERATION */ + xf_emit(ctx, 1, 0xcf); /* 000000ff SIFC_FORMAT */ + xf_emit(ctx, 1, 0xcf); /* 000000ff DRAW_COLOR_FORMAT */ + xf_emit(ctx, 1, 0xcf); /* 000000ff SRC_FORMAT */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + xf_emit(ctx, 1, 0); /* 7/f[NVA3] MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ + xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 8, 1); /* 00000001 UNK19E0 */ + xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + if (device->chipset == 0x50) + xf_emit(ctx, 1, 0); /* ff */ + else + xf_emit(ctx, 3, 0); /* 1, 7, 3ff */ + xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ + xf_emit(ctx, 1, 0); /* 000fffff BLIT_DU_DX_FRACT */ + xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DU_DX_INT */ + xf_emit(ctx, 1, 0); /* 000fffff BLIT_DV_DY_FRACT */ + xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DV_DY_INT */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, magic1); /* 3ff/7ff tesla UNK0D68 */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 8, 0); /* 0000ffff DMA_COLOR */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_GLOBAL */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_LOCAL */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_STACK */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_DST */ + xf_emit(ctx, 1, 0); /* 7 */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 8, 0); /* 000000ff RT_ADDRESS_HIGH */ + xf_emit(ctx, 8, 0); /* ffffffff RT_LAYER_STRIDE */ + xf_emit(ctx, 8, 0); /* ffffffff RT_ADDRESS_LOW */ + xf_emit(ctx, 8, 8); /* 0000007f RT_TILE_MODE */ + xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 8, 0x400); /* 0fffffff RT_HORIZ */ + xf_emit(ctx, 8, 0x300); /* 0000ffff RT_VERT */ + xf_emit(ctx, 1, 1); /* 00001fff RT_ARRAY_MODE */ + xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 1, 0x20); /* 00000fff DST_TILE_MODE */ + xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ + xf_emit(ctx, 1, 0x100); /* 0001ffff DST_HEIGHT */ + xf_emit(ctx, 1, 0); /* 000007ff DST_LAYER */ + xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ + xf_emit(ctx, 1, 0); /* ffffffff DST_ADDRESS_LOW */ + xf_emit(ctx, 1, 0); /* 000000ff DST_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0x40); /* 0007ffff DST_PITCH */ + xf_emit(ctx, 1, 0x100); /* 0001ffff DST_WIDTH */ + xf_emit(ctx, 1, 0); /* 0000ffff */ + xf_emit(ctx, 1, 3); /* 00000003 tesla UNK15AC */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ + xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ + xf_emit(ctx, 1, 0); /* 00000007 */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_ZETA */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 2, 0); /* ffff, ff/3ff */ + xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0); /* ffffffff ZETA_LAYER_STRIDE */ + xf_emit(ctx, 1, 0); /* 000000ff ZETA_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff ZETA_ADDRESS_LOW */ + xf_emit(ctx, 1, 4); /* 00000007 ZETA_TILE_MODE */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + xf_emit(ctx, 1, 0x400); /* 0fffffff ZETA_HORIZ */ + xf_emit(ctx, 1, 0x300); /* 0000ffff ZETA_VERT */ + xf_emit(ctx, 1, 0x1001); /* 00001fff ZETA_ARRAY_MODE */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 0); /* 00000001 */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ + xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ + xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */ + xf_emit(ctx, 1, 0); /* 7 */ + xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + } + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 1, 0x0fac6881); /* fffffff */ + xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ + xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ + xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ + xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, 0); /* 0000000f tesla UNK15C8 */ + } + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + if (device->chipset >= 0xa0) { + xf_emit(ctx, 3, 0); /* 7/f, 1, ffff0ff3 */ + xf_emit(ctx, 1, 0xfac6881); /* fffffff */ + xf_emit(ctx, 4, 0); /* 1, 1, 1, 3ff */ + xf_emit(ctx, 1, 4); /* 7 */ + xf_emit(ctx, 1, 0); /* 1 */ + xf_emit(ctx, 2, 1); /* 1 */ + xf_emit(ctx, 2, 0); /* 7, f */ + xf_emit(ctx, 1, 1); /* 1 */ + xf_emit(ctx, 1, 0); /* 7/f */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 0x9, 0); /* 1 */ + else + xf_emit(ctx, 0x8, 0); /* 1 */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 8, 1); /* 1 */ + xf_emit(ctx, 1, 0x11); /* 7f */ + xf_emit(ctx, 7, 0); /* 7f */ + xf_emit(ctx, 1, 0xfac6881); /* fffffff */ + xf_emit(ctx, 1, 0xf); /* f */ + xf_emit(ctx, 7, 0); /* f */ + xf_emit(ctx, 1, 0x11); /* 7f */ + xf_emit(ctx, 1, 1); /* 1 */ + xf_emit(ctx, 5, 0); /* 1, 7, 3ff, 3, 7 */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + } + } +} + +static void +nv50_gr_construct_xfer_tex(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + xf_emit(ctx, 2, 0); /* 1 LINKED_TSC. yes, 2. */ + if (device->chipset != 0x50) + xf_emit(ctx, 1, 0); /* 3 */ + xf_emit(ctx, 1, 1); /* 1ffff BLIT_DU_DX_INT */ + xf_emit(ctx, 1, 0); /* fffff BLIT_DU_DX_FRACT */ + xf_emit(ctx, 1, 1); /* 1ffff BLIT_DV_DY_INT */ + xf_emit(ctx, 1, 0); /* fffff BLIT_DV_DY_FRACT */ + if (device->chipset == 0x50) + xf_emit(ctx, 1, 0); /* 3 BLIT_CONTROL */ + else + xf_emit(ctx, 2, 0); /* 3ff, 1 */ + xf_emit(ctx, 1, 0x2a712488); /* ffffffff SRC_TIC_0 */ + xf_emit(ctx, 1, 0); /* ffffffff SRC_TIC_1 */ + xf_emit(ctx, 1, 0x4085c000); /* ffffffff SRC_TIC_2 */ + xf_emit(ctx, 1, 0x40); /* ffffffff SRC_TIC_3 */ + xf_emit(ctx, 1, 0x100); /* ffffffff SRC_TIC_4 */ + xf_emit(ctx, 1, 0x10100); /* ffffffff SRC_TIC_5 */ + xf_emit(ctx, 1, 0x02800000); /* ffffffff SRC_TIC_6 */ + xf_emit(ctx, 1, 0); /* ffffffff SRC_TIC_7 */ + if (device->chipset == 0x50) { + xf_emit(ctx, 1, 0); /* 00000001 turing UNK358 */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34? */ + xf_emit(ctx, 1, 0); /* 00000003 turing UNK37C tesla UNK1690 */ + xf_emit(ctx, 1, 0); /* 00000003 BLIT_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000001 turing UNK32C tesla UNK0F94 */ + } else if (!IS_NVAAF(device->chipset)) { + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34? */ + xf_emit(ctx, 1, 0); /* 00000003 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + xf_emit(ctx, 1, 0); /* 00000003 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1664 / turing UNK03E8 */ + xf_emit(ctx, 1, 0); /* 00000003 */ + xf_emit(ctx, 1, 0); /* 000003ff */ + } else { + xf_emit(ctx, 0x6, 0); + } + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34 */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_TEXTURE */ + xf_emit(ctx, 1, 0); /* 0000ffff DMA_SRC */ +} + +static void +nv50_gr_construct_xfer_unk8cxx(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 2, 0); /* 7, ffff0ff3 */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE */ + xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0D64 */ + xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0DF4 */ + xf_emit(ctx, 1, 1); /* 00000001 UNK15B4 */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ + xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK0F98 */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1668 */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ + xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ + xf_emit(ctx, 1, 0); /* 00000001 POLYGON_SMOOTH_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1658 */ + xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ + xf_emit(ctx, 1, 0); /* ffff0ff3 */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE */ + xf_emit(ctx, 1, 1); /* 00000001 UNK15B4 */ + xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */ + xf_emit(ctx, 1, 1); /* 00000001 tesla UNK165C */ + xf_emit(ctx, 1, 0x30201000); /* ffffffff tesla UNK1670 */ + xf_emit(ctx, 1, 0x70605040); /* ffffffff tesla UNK1670 */ + xf_emit(ctx, 1, 0xb8a89888); /* ffffffff tesla UNK1670 */ + xf_emit(ctx, 1, 0xf8e8d8c8); /* ffffffff tesla UNK1670 */ + xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ + xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ +} + +static void +nv50_gr_construct_xfer_tp(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + if (device->chipset < 0xa0) { + nv50_gr_construct_xfer_unk84xx(ctx); + nv50_gr_construct_xfer_tprop(ctx); + nv50_gr_construct_xfer_tex(ctx); + nv50_gr_construct_xfer_unk8cxx(ctx); + } else { + nv50_gr_construct_xfer_tex(ctx); + nv50_gr_construct_xfer_tprop(ctx); + nv50_gr_construct_xfer_unk8cxx(ctx); + nv50_gr_construct_xfer_unk84xx(ctx); + } +} + +static void +nv50_gr_construct_xfer_mpc(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i, mpcnt = 2; + switch (device->chipset) { + case 0x98: + case 0xaa: + mpcnt = 1; + break; + case 0x50: + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0xa8: + case 0xac: + mpcnt = 2; + break; + case 0xa0: + case 0xa3: + case 0xa5: + case 0xaf: + mpcnt = 3; + break; + } + for (i = 0; i < mpcnt; i++) { + xf_emit(ctx, 1, 0); /* ff */ + xf_emit(ctx, 1, 0x80); /* ffffffff tesla UNK1404 */ + xf_emit(ctx, 1, 0x80007004); /* ffffffff tesla UNK12B0 */ + xf_emit(ctx, 1, 0x04000400); /* ffffffff */ + if (device->chipset >= 0xa0) + xf_emit(ctx, 1, 0xc0); /* 00007fff tesla UNK152C */ + xf_emit(ctx, 1, 0x1000); /* 0000ffff tesla UNK0D60 */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + if (device->chipset == 0x86 || device->chipset == 0x98 || device->chipset == 0xa8 || IS_NVAAF(device->chipset)) { + xf_emit(ctx, 1, 0xe00); /* 7fff */ + xf_emit(ctx, 1, 0x1e00); /* 7fff */ + } + xf_emit(ctx, 1, 1); /* 000000ff VP_REG_ALLOC_TEMP */ + xf_emit(ctx, 1, 0); /* 00000001 LINKED_TSC */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + if (device->chipset == 0x50) + xf_emit(ctx, 2, 0x1000); /* 7fff tesla UNK141C */ + xf_emit(ctx, 1, 1); /* 000000ff GP_REG_ALLOC_TEMP */ + xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + xf_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */ + xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */ + if (IS_NVAAF(device->chipset)) + xf_emit(ctx, 0xb, 0); /* RO */ + else if (device->chipset >= 0xa0) + xf_emit(ctx, 0xc, 0); /* RO */ + else + xf_emit(ctx, 0xa, 0); /* RO */ + } + xf_emit(ctx, 1, 0x08100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + if (device->chipset >= 0xa0) { + xf_emit(ctx, 1, 0x1fe21); /* 0003ffff tesla UNK0FAC */ + } + xf_emit(ctx, 3, 0); /* 7fff, 0, 0 */ + xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ + xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ + xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ + xf_emit(ctx, 1, 1); /* 00000001 LANES32 */ + xf_emit(ctx, 1, 0x10001); /* 00ffffff BLOCK_ALLOC */ + xf_emit(ctx, 1, 0x10001); /* ffffffff BLOCKDIM_XY */ + xf_emit(ctx, 1, 1); /* 0000ffff BLOCKDIM_Z */ + xf_emit(ctx, 1, 0); /* ffffffff SHARED_SIZE */ + xf_emit(ctx, 1, 0x1fe21); /* 1ffff/3ffff[NVA0+] tesla UNk0FAC */ + xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34 */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + xf_emit(ctx, 1, 0); /* ff/3ff */ + xf_emit(ctx, 1, 0); /* 1 LINKED_TSC */ + xf_emit(ctx, 1, 0); /* ff FP_ADDRESS_HIGH */ + xf_emit(ctx, 1, 0); /* ffffffff FP_ADDRESS_LOW */ + xf_emit(ctx, 1, 0x08100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ + xf_emit(ctx, 1, 0); /* 000000ff FRAG_COLOR_CLAMP_EN */ + xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */ + xf_emit(ctx, 1, 0x11); /* 0000007f RT_FORMAT */ + xf_emit(ctx, 7, 0); /* 0000007f RT_FORMAT */ + xf_emit(ctx, 1, 0); /* 00000007 */ + xf_emit(ctx, 1, 0xfac6881); /* 0fffffff RT_CONTROL */ + xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */ + if (IS_NVA3F(device->chipset)) + xf_emit(ctx, 1, 3); /* 00000003 tesla UNK16B4 */ + xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ + xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */ + xf_emit(ctx, 1, 4); /* ffffffff tesla UNK1400 */ + xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ + xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ + xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ + xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ + if (IS_NVA3F(device->chipset)) { + xf_emit(ctx, 1, 0); /* 00000001 UNK12E4 */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ + xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */ + xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */ + xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ + xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1928 */ + xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ + } + xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0F90 */ + xf_emit(ctx, 1, 4); /* 000000ff FP_RESULT_COUNT */ + /* XXX: demagic this part some day */ + if (device->chipset == 0x50) + xf_emit(ctx, 0x3a0, 0); + else if (device->chipset < 0x94) + xf_emit(ctx, 0x3a2, 0); + else if (device->chipset == 0x98 || device->chipset == 0xaa) + xf_emit(ctx, 0x39f, 0); + else + xf_emit(ctx, 0x3a3, 0); + xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ + xf_emit(ctx, 1, 0); /* 7 OPERATION */ + xf_emit(ctx, 1, 1); /* 1 DST_LINEAR */ + xf_emit(ctx, 0x2d, 0); +} + +static void +nv50_gr_construct_xfer2(struct nvkm_grctx *ctx) +{ + struct nvkm_device *device = ctx->device; + int i; + u32 offset; + u32 units = nvkm_rd32(device, 0x1540); + int size = 0; + + offset = (ctx->ctxvals_pos+0x3f)&~0x3f; + + if (device->chipset < 0xa0) { + for (i = 0; i < 8; i++) { + ctx->ctxvals_pos = offset + i; + /* that little bugger belongs to csched. No idea + * what it's doing here. */ + if (i == 0) + xf_emit(ctx, 1, 0x08100c12); /* FP_INTERPOLANT_CTRL */ + if (units & (1 << i)) + nv50_gr_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + } else { + /* Strand 0: TPs 0, 1 */ + ctx->ctxvals_pos = offset; + /* that little bugger belongs to csched. No idea + * what it's doing here. */ + xf_emit(ctx, 1, 0x08100c12); /* FP_INTERPOLANT_CTRL */ + if (units & (1 << 0)) + nv50_gr_construct_xfer_mpc(ctx); + if (units & (1 << 1)) + nv50_gr_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 1: TPs 2, 3 */ + ctx->ctxvals_pos = offset + 1; + if (units & (1 << 2)) + nv50_gr_construct_xfer_mpc(ctx); + if (units & (1 << 3)) + nv50_gr_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 2: TPs 4, 5, 6 */ + ctx->ctxvals_pos = offset + 2; + if (units & (1 << 4)) + nv50_gr_construct_xfer_mpc(ctx); + if (units & (1 << 5)) + nv50_gr_construct_xfer_mpc(ctx); + if (units & (1 << 6)) + nv50_gr_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 3: TPs 7, 8, 9 */ + ctx->ctxvals_pos = offset + 3; + if (units & (1 << 7)) + nv50_gr_construct_xfer_mpc(ctx); + if (units & (1 << 8)) + nv50_gr_construct_xfer_mpc(ctx); + if (units & (1 << 9)) + nv50_gr_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + ctx->ctxvals_pos = offset + size * 8; + ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f; + cp_lsr (ctx, offset); + cp_out (ctx, CP_SET_XFER_POINTER); + cp_lsr (ctx, size); + cp_out (ctx, CP_SEEK_2); + cp_out (ctx, CP_XFER_2); + cp_wait(ctx, XFER, BUSY); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxtu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxtu102.c new file mode 100644 index 000000000..2299ca07d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxtu102.c @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ctxgf100.h" + +static void +tu102_grctx_generate_r419c0c(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419c0c, 0x80000000, 0x80000000); + nvkm_mask(device, 0x40584c, 0x00000008, 0x00000000); + nvkm_mask(device, 0x400080, 0x00000000, 0x00000000); +} + +static void +tu102_grctx_generate_sm_id(struct gf100_gr *gr, int gpc, int tpc, int sm) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x608), sm); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x088), sm); +} + +static const struct gf100_gr_init +tu102_grctx_init_unknown_bundle_init_0[] = { + { 0x00001000, 1, 0x00000001, 0x00000004 }, + { 0x00002020, 64, 0x00000001, 0x00000000 }, + { 0x0001e100, 1, 0x00000001, 0x00000001 }, + {} +}; + +static const struct gf100_gr_pack +tu102_grctx_pack_sw_veid_bundle_init[] = { + { gv100_grctx_init_sw_veid_bundle_init_0 }, + { tu102_grctx_init_unknown_bundle_init_0 }, + {} +}; + +static void +tu102_grctx_generate_attrib(struct gf100_grctx *info) +{ + const u64 size = 0x80000; /*XXX: educated guess */ + const int s = 8; + const int b = mmio_vram(info, size, (1 << s), true); + + gv100_grctx_generate_attrib(info); + + mmio_refn(info, 0x408070, 0x00000000, s, b); + mmio_wr32(info, 0x408074, size >> s); /*XXX: guess */ + mmio_refn(info, 0x419034, 0x00000000, s, b); + mmio_wr32(info, 0x408078, 0x00000000); +} + +const struct gf100_grctx_func +tu102_grctx = { + .unkn88c = gv100_grctx_unkn88c, + .main = gf100_grctx_generate_main, + .unkn = gv100_grctx_generate_unkn, + .sw_veid_bundle_init = tu102_grctx_pack_sw_veid_bundle_init, + .bundle = gm107_grctx_generate_bundle, + .bundle_size = 0x3000, + .bundle_min_gpm_fifo_depth = 0x180, + .bundle_token_limit = 0xa80, + .pagepool = gp100_grctx_generate_pagepool, + .pagepool_size = 0x20000, + .attrib = tu102_grctx_generate_attrib, + .attrib_nr_max = 0x800, + .attrib_nr = 0x700, + .alpha_nr_max = 0xc00, + .alpha_nr = 0x800, + .gfxp_nr = 0xfa8, + .sm_id = tu102_grctx_generate_sm_id, + .skip_pd_num_tpc_per_gpc = true, + .rop_mapping = gv100_grctx_generate_rop_mapping, + .r406500 = gm200_grctx_generate_r406500, + .r400088 = gv100_grctx_generate_r400088, + .r419c0c = tu102_grctx_generate_r419c0c, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc new file mode 100644 index 000000000..64208bf95 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/com.fuc @@ -0,0 +1,335 @@ +/* fuc microcode util functions for gf100 PGRAPH + * + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#ifdef INCLUDE_CODE +// queue_put - add request to queue +// +// In : $r13 queue pointer +// $r14 command +// $r15 data +// +queue_put: + // make sure we have space.. + ld b32 $r8 D[$r13 + 0x0] // GET + ld b32 $r9 D[$r13 + 0x4] // PUT + xor $r8 8 + cmpu b32 $r8 $r9 + bra ne #queue_put_next + mov $r15 E_CMD_OVERFLOW + call(error) + ret + + // store cmd/data on queue + queue_put_next: + and $r8 $r9 7 + shl b32 $r8 3 + add b32 $r8 $r13 + add b32 $r8 8 + st b32 D[$r8 + 0x0] $r14 + st b32 D[$r8 + 0x4] $r15 + + // update PUT + add b32 $r9 1 + and $r9 0xf + st b32 D[$r13 + 0x4] $r9 + ret + +// queue_get - fetch request from queue +// +// In : $r13 queue pointer +// +// Out: $p1 clear on success (data available) +// $r14 command +// $r15 data +// +queue_get: + bset $flags $p1 + ld b32 $r8 D[$r13 + 0x0] // GET + ld b32 $r9 D[$r13 + 0x4] // PUT + cmpu b32 $r8 $r9 + bra e #queue_get_done + // fetch first cmd/data pair + and $r9 $r8 7 + shl b32 $r9 3 + add b32 $r9 $r13 + add b32 $r9 8 + ld b32 $r14 D[$r9 + 0x0] + ld b32 $r15 D[$r9 + 0x4] + + // update GET + add b32 $r8 1 + and $r8 0xf + st b32 D[$r13 + 0x0] $r8 + bclr $flags $p1 +queue_get_done: + ret + +// nv_rd32 - read 32-bit value from nv register +// +// In : $r14 register +// Out: $r15 value +// +nv_rd32: + mov b32 $r12 $r14 + bset $r12 31 // MMIO_CTRL_PENDING + nv_iowr(NV_PGRAPH_FECS_MMIO_CTRL, 0, $r12) + nv_rd32_wait: + nv_iord($r12, NV_PGRAPH_FECS_MMIO_CTRL, 0) + xbit $r12 $r12 31 + bra ne #nv_rd32_wait + mov $r10 6 // DONE_MMIO_RD + call(wait_doneo) + nv_iord($r15, NV_PGRAPH_FECS_MMIO_RDVAL, 0) + ret + +// nv_wr32 - write 32-bit value to nv register +// +// In : $r14 register +// $r15 value +// +nv_wr32: + nv_iowr(NV_PGRAPH_FECS_MMIO_WRVAL, 0, $r15) + mov b32 $r12 $r14 + bset $r12 31 // MMIO_CTRL_PENDING + bset $r12 30 // MMIO_CTRL_WRITE + nv_iowr(NV_PGRAPH_FECS_MMIO_CTRL, 0, $r12) + nv_wr32_wait: + nv_iord($r12, NV_PGRAPH_FECS_MMIO_CTRL, 0) + xbit $r12 $r12 31 + bra ne #nv_wr32_wait + ret + +// wait_donez - wait on FUC_DONE bit to become clear +// +// In : $r10 bit to wait on +// +wait_donez: + trace_set(T_WAIT); + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(6), 0, $r10) + wait_donez_ne: + nv_iord($r8, NV_PGRAPH_FECS_SIGNAL, 0) + xbit $r8 $r8 $r10 + bra ne #wait_donez_ne + trace_clr(T_WAIT) + ret + +// wait_doneo - wait on FUC_DONE bit to become set +// +// In : $r10 bit to wait on +// +wait_doneo: + trace_set(T_WAIT); + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(6), 0, $r10) + wait_doneo_e: + nv_iord($r8, NV_PGRAPH_FECS_SIGNAL, 0) + xbit $r8 $r8 $r10 + bra e #wait_doneo_e + trace_clr(T_WAIT) + ret + +// mmctx_size - determine size of a mmio list transfer +// +// In : $r14 mmio list head +// $r15 mmio list tail +// Out: $r15 transfer size (in bytes) +// +mmctx_size: + clear b32 $r9 + nv_mmctx_size_loop: + ld b32 $r8 D[$r14] + shr b32 $r8 26 + add b32 $r8 1 + shl b32 $r8 2 + add b32 $r9 $r8 + add b32 $r14 4 + cmpu b32 $r14 $r15 + bra ne #nv_mmctx_size_loop + mov b32 $r15 $r9 + ret + +// mmctx_xfer - execute a list of mmio transfers +// +// In : $r10 flags +// bit 0: direction (0 = save, 1 = load) +// bit 1: set if first transfer +// bit 2: set if last transfer +// $r11 base +// $r12 mmio list head +// $r13 mmio list tail +// $r14 multi_stride +// $r15 multi_mask +// +mmctx_xfer: + trace_set(T_MMCTX) + clear b32 $r9 + or $r11 $r11 + bra e #mmctx_base_disabled + nv_iowr(NV_PGRAPH_FECS_MMCTX_BASE, 0, $r11) + bset $r9 0 // BASE_EN + mmctx_base_disabled: + or $r14 $r14 + bra e #mmctx_multi_disabled + nv_iowr(NV_PGRAPH_FECS_MMCTX_MULTI_STRIDE, 0, $r14) + nv_iowr(NV_PGRAPH_FECS_MMCTX_MULTI_MASK, 0, $r15) + bset $r9 1 // MULTI_EN + mmctx_multi_disabled: + + xbit $r11 $r10 0 + shl b32 $r11 16 // DIR + bset $r11 12 // QLIMIT = 0x10 + xbit $r14 $r10 1 + shl b32 $r14 17 + or $r11 $r14 // START_TRIGGER + nv_iowr(NV_PGRAPH_FECS_MMCTX_CTRL, 0, $r11) + + // loop over the mmio list, and send requests to the hw + mmctx_exec_loop: + // wait for space in mmctx queue + mmctx_wait_free: + nv_iord($r14, NV_PGRAPH_FECS_MMCTX_CTRL, 0) + and $r14 0x1f + bra e #mmctx_wait_free + + // queue up an entry + ld b32 $r14 D[$r12] + or $r14 $r9 + nv_iowr(NV_PGRAPH_FECS_MMCTX_QUEUE, 0, $r14) + add b32 $r12 4 + cmpu b32 $r12 $r13 + bra ne #mmctx_exec_loop + + xbit $r11 $r10 2 + bra ne #mmctx_stop + // wait for queue to empty + mmctx_fini_wait: + nv_iord($r11, NV_PGRAPH_FECS_MMCTX_CTRL, 0) + and $r11 0x1f + cmpu b32 $r11 0x10 + bra ne #mmctx_fini_wait + mov $r10 5 // DONE_MMCTX + call(wait_donez) + bra #mmctx_done + mmctx_stop: + xbit $r11 $r10 0 + shl b32 $r11 16 // DIR + bset $r11 12 // QLIMIT = 0x10 + bset $r11 18 // STOP_TRIGGER + nv_iowr(NV_PGRAPH_FECS_MMCTX_CTRL, 0, $r11) + mmctx_stop_wait: + // wait for STOP_TRIGGER to clear + nv_iord($r11, NV_PGRAPH_FECS_MMCTX_CTRL, 0) + xbit $r11 $r11 18 + bra ne #mmctx_stop_wait + mmctx_done: + trace_clr(T_MMCTX) + ret + +// Wait for DONE_STRAND +// +strand_wait: + push $r10 + mov $r10 2 + call(wait_donez) + pop $r10 + ret + +// unknown - call before issuing strand commands +// +strand_pre: + mov $r9 NV_PGRAPH_FECS_STRAND_CMD_ENABLE + nv_iowr(NV_PGRAPH_FECS_STRAND_CMD, 0x3f, $r9) + call(strand_wait) + ret + +// unknown - call after issuing strand commands +// +strand_post: + mov $r9 NV_PGRAPH_FECS_STRAND_CMD_DISABLE + nv_iowr(NV_PGRAPH_FECS_STRAND_CMD, 0x3f, $r9) + call(strand_wait) + ret + +// Selects strand set?! +// +// In: $r14 id +// +strand_set: + mov $r12 0xf + nv_iowr(NV_PGRAPH_FECS_STRAND_FILTER, 0x3f, $r12) + mov $r12 NV_PGRAPH_FECS_STRAND_CMD_DEACTIVATE_FILTER + nv_iowr(NV_PGRAPH_FECS_STRAND_CMD, 0x3f, $r12) + nv_iowr(NV_PGRAPH_FECS_STRAND_FILTER, 0x3f, $r14) + mov $r12 NV_PGRAPH_FECS_STRAND_CMD_ACTIVATE_FILTER + nv_iowr(NV_PGRAPH_FECS_STRAND_CMD, 0x3f, $r12) + call(strand_wait) + ret + +// Initialise strand context data +// +// In : $r15 context base +// Out: $r15 context size (in bytes) +// +// Strandset(?) 3 hardcoded currently +// +strand_ctx_init: + trace_set(T_STRINIT) + call(strand_pre) + mov $r14 3 + call(strand_set) + + clear b32 $r12 + nv_iowr(NV_PGRAPH_FECS_STRAND_SELECT, 0x3f, $r12) + mov $r12 NV_PGRAPH_FECS_STRAND_CMD_SEEK + nv_iowr(NV_PGRAPH_FECS_STRAND_CMD, 0x3f, $r12) + call(strand_wait) + sub b32 $r12 $r0 1 + nv_iowr(NV_PGRAPH_FECS_STRAND_DATA, 0x3f, $r12) + mov $r12 NV_PGRAPH_FECS_STRAND_CMD_GET_INFO + nv_iowr(NV_PGRAPH_FECS_STRAND_CMD, 0x3f, $r12) + call(strand_wait) + call(strand_post) + + // read the size of each strand, poke the context offset of + // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry + // about it later then. + nv_mkio($r8, NV_PGRAPH_FECS_STRAND_SAVE_SWBASE, 0x00) + nv_iord($r9, NV_PGRAPH_FECS_STRANDS_CNT, 0x00) + shr b32 $r14 $r15 8 + ctx_init_strand_loop: + iowr I[$r8 + 0x000] $r14 // STRAND_SAVE_SWBASE + iowr I[$r8 + 0x100] $r14 // STRAND_LOAD_SWBASE + iord $r10 I[$r8 + 0x200] // STRAND_SIZE + shr b32 $r10 6 + add b32 $r10 1 + add b32 $r14 $r10 + add b32 $r8 4 + sub b32 $r9 1 + bra ne #ctx_init_strand_loop + + shl b32 $r14 8 + sub b32 $r15 $r14 $r15 + trace_clr(T_STRINIT) + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc new file mode 100644 index 000000000..4984b0069 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc @@ -0,0 +1,492 @@ +/* fuc microcode for gf100 PGRAPH/GPC + * + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +/* TODO + * - bracket certain functions with scratch writes, useful for debugging + * - watchdog timer around ctx operations + */ + +#ifdef INCLUDE_DATA +gpc_mmio_list_head: .b32 #mmio_list_base +gpc_mmio_list_tail: +tpc_mmio_list_head: .b32 #mmio_list_base +tpc_mmio_list_tail: +unk_mmio_list_head: .b32 #mmio_list_base +unk_mmio_list_tail: .b32 #mmio_list_base + +gpc_id: .b32 0 + +tpc_count: .b32 0 +tpc_mask: .b32 0 + +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 +unk_count: .b32 0 +unk_mask: .b32 0 +#endif + +cmd_queue: queue_init + +mmio_list_base: +#endif + +#ifdef INCLUDE_CODE +#define gpc_addr(reg,addr) /* +*/ imm32(reg,addr) /* +*/ or reg NV_PGRAPH_GPCX_GPCCS_MMIO_CTRL_BASE_ENABLE +#define gpc_wr32(addr,reg) /* +*/ gpc_addr($r14,addr) /* +*/ mov b32 $r15 reg /* +*/ call(nv_wr32) + +// reports an exception to the host +// +// In: $r15 error code (see os.h) +// +error: + push $r14 + nv_wr32(NV_PGRAPH_FECS_CC_SCRATCH_VAL(5), $r15) + mov $r15 1 + nv_wr32(NV_PGRAPH_FECS_INTR_UP_SET, $r15) + pop $r14 + ret + +#if CHIPSET >= GM107 +tpc_strand_wait: + push $r9 + trace_set(T_STRTPC) + tpc_strand_busy: + nv_iord($r9, NV_PGRAPH_GPCX_GPCCS_TPC_STATUS, 0) + bra b32 $r9 0x0 ne #tpc_strand_busy + trace_clr(T_STRTPC) + pop $r9 + ret + +#define tpc_strand_wait() call(tpc_strand_wait) +#define tpc_strand_enable() /* +*/ mov $r15 NV_PGRAPH_GPC0_TPCX_STRAND_CMD_ENABLE /* +*/ gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_CMD, $r15) /* +*/ tpc_strand_wait() +#define tpc_strand_disable() /* +*/ mov $r15 NV_PGRAPH_GPC0_TPCX_STRAND_CMD_DISABLE /* +*/ gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_CMD, $r15) /* +*/ tpc_strand_wait() +#define tpc_strand_seek(p) /* +*/ mov $r15 NV_PGRAPH_GPC0_TPCX_STRAND_INDEX_ALL /* +*/ gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_INDEX, $r15) /* +*/ mov $r15 p /* +*/ gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_SELECT, $r15) /* +*/ mov $r15 NV_PGRAPH_GPC0_TPCX_STRAND_CMD_SEEK /* +*/ tpc_strand_wait() +#define tpc_strand_info(m) /* +*/ gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_CMD, $r15) /* +*/ mov $r15 m /* +*/ gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_DATA, $r15) /* +*/ mov $r15 NV_PGRAPH_GPC0_TPCX_STRAND_CMD_GET_INFO /* +*/ gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_CMD, $r15) /* +*/ tpc_strand_wait() +#endif + + +// GPC fuc initialisation, executed by triggering ucode start, will +// fall through to main loop after completion. +// +// Input: +// CC_SCRATCH[1]: context base +// +// Output: +// CC_SCRATCH[0]: +// 31:31: set to signal completion +// CC_SCRATCH[1]: +// 31:0: GPC context size +// +init: + clear b32 $r0 + + // setup stack + nv_iord($r1, NV_PGRAPH_GPCX_GPCCS_CAPS, 0) + extr $r1 $r1 9:17 + shl b32 $r1 8 + mov $sp $r1 + + // enable fifo access + mov $r2 NV_PGRAPH_GPCX_GPCCS_ACCESS_FIFO + nv_iowr(NV_PGRAPH_GPCX_GPCCS_ACCESS, 0, $r2) + + // setup i0 handler, and route all interrupts to it + mov $r1 #ih + mov $iv0 $r1 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_INTR_ROUTE, 0, $r0) + + // enable fifo interrupt + mov $r2 NV_PGRAPH_GPCX_GPCCS_INTR_EN_SET_FIFO + nv_iowr(NV_PGRAPH_GPCX_GPCCS_INTR_EN_SET, 0, $r2) + + // enable interrupts + bset $flags ie0 + + // how many TPCs do we have? + nv_iord($r2, NV_PGRAPH_GPCX_GPCCS_UNITS, 0) + mov $r3 1 + and $r2 0x1f + shl b32 $r3 $r2 + sub b32 $r3 1 + st b32 D[$r0 + #tpc_count] $r2 + st b32 D[$r0 + #tpc_mask] $r3 + + // determine which GPC we are, setup (optional) mmio access offset + nv_iord($r2, NV_PGRAPH_GPCX_GPCCS_MYINDEX, 0) + st b32 D[$r0 + #gpc_id] $r2 + shl b32 $r2 15 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_MMIO_BASE, 0, $r2) + +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 + // figure out which, and how many, UNKs are actually present + gpc_addr($r14, 0x500c30) + clear b32 $r2 + clear b32 $r3 + clear b32 $r4 + init_unk_loop: + call(nv_rd32) + cmp b32 $r15 0 + bra z #init_unk_next + mov $r15 1 + shl b32 $r15 $r2 + or $r4 $r15 + add b32 $r3 1 + init_unk_next: + add b32 $r2 1 + add b32 $r14 4 + cmp b32 $r2 NV_PGRAPH_GPCX_UNK__SIZE + bra ne #init_unk_loop + init_unk_done: + st b32 D[$r0 + #unk_count] $r3 + st b32 D[$r0 + #unk_mask] $r4 +#endif + + // initialise context base, and size tracking + nv_iord($r2, NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(1), 0) + clear b32 $r3 // track GPC context size here + + // set mmctx base addresses now so we don't have to do it later, + // they don't currently ever change + shr b32 $r5 $r2 8 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_MMCTX_SAVE_SWBASE, 0, $r5) + nv_iowr(NV_PGRAPH_GPCX_GPCCS_MMCTX_LOAD_SWBASE, 0, $r5) + + // calculate GPC mmio context size + ld b32 $r14 D[$r0 + #gpc_mmio_list_head] + ld b32 $r15 D[$r0 + #gpc_mmio_list_tail] + call(mmctx_size) + add b32 $r2 $r15 + add b32 $r3 $r15 + + // calculate per-TPC mmio context size + ld b32 $r14 D[$r0 + #tpc_mmio_list_head] + ld b32 $r15 D[$r0 + #tpc_mmio_list_tail] + call(mmctx_size) + ld b32 $r14 D[$r0 + #tpc_count] + mulu $r14 $r15 + add b32 $r2 $r14 + add b32 $r3 $r14 + +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 + // calculate per-UNK mmio context size + ld b32 $r14 D[$r0 + #unk_mmio_list_head] + ld b32 $r15 D[$r0 + #unk_mmio_list_tail] + call(mmctx_size) + ld b32 $r14 D[$r0 + #unk_count] + mulu $r14 $r15 + add b32 $r2 $r14 + add b32 $r3 $r14 +#endif + + // round up base/size to 256 byte boundary (for strand SWBASE) + shr b32 $r3 2 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_MMCTX_LOAD_COUNT, 0, $r3) // wtf for?! + shr b32 $r2 8 + shr b32 $r3 6 + add b32 $r2 1 + add b32 $r3 1 + shl b32 $r2 8 + shl b32 $r3 8 + + // calculate size of strand context data + mov b32 $r15 $r2 + call(strand_ctx_init) + add b32 $r2 $r15 + add b32 $r3 $r15 + +#if CHIPSET >= GM107 + // calculate size of tpc strand context data + mov $r15 NV_PGRAPH_GPC0_TPCX_STRAND_INDEX_ALL + gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_INDEX, $r15) + tpc_strand_enable(); + tpc_strand_seek(0); + tpc_strand_info(-1); + + ld b32 $r4 D[$r0 + #tpc_count] + gpc_addr($r5, NV_PGRAPH_GPC0_TPC0) + tpc_strand_init_tpc_loop: + add b32 $r14 $r5 NV_TPC_STRAND_CNT + call(nv_rd32) + mov b32 $r6 $r15 + clear b32 $r7 + tpc_strand_init_idx_loop: + add b32 $r14 $r5 NV_TPC_STRAND_INDEX + mov b32 $r15 $r7 + call(nv_wr32) + add b32 $r14 $r5 NV_TPC_STRAND_SAVE_SWBASE + shr b32 $r15 $r2 8 + call(nv_wr32) + add b32 $r14 $r5 NV_TPC_STRAND_LOAD_SWBASE + shr b32 $r15 $r2 8 + call(nv_wr32) + add b32 $r14 $r5 NV_TPC_STRAND_WORDS + call(nv_rd32) + shr b32 $r15 6 + add b32 $r15 1 + shl b32 $r15 8 + add b32 $r2 $r15 + add b32 $r3 $r15 + add b32 $r7 1 + sub b32 $r6 1 + bra nz #tpc_strand_init_idx_loop + add b32 $r5 NV_PGRAPH_GPC0_TPC0__SIZE + sub b32 $r4 1 + bra nz #tpc_strand_init_tpc_loop + + mov $r15 NV_PGRAPH_GPC0_TPCX_STRAND_INDEX_ALL + gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_INDEX, $r15) + tpc_strand_disable(); +#endif + + // save context size, and tell HUB we're done + nv_iowr(NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(1), 0, $r3) + clear b32 $r2 + bset $r2 31 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(0), 0, $r2) + +// Main program loop, very simple, sleeps until woken up by the interrupt +// handler, pulls a command from the queue and executes its handler +// +wait: + sleep $p0 + bset $flags $p0 +main: + mov $r13 #cmd_queue + call(queue_get) + bra $p1 #wait + + // 0x0000-0x0003 are all context transfers + cmpu b32 $r14 0x04 + bra nc #main_not_ctx_xfer + // fetch $flags and mask off $p1/$p2 + mov $r1 $flags + mov $r2 0x0006 + not b32 $r2 + and $r1 $r2 + // set $p1/$p2 according to transfer type + shl b32 $r14 1 + or $r1 $r14 + mov $flags $r1 + // transfer context data + call(ctx_xfer) + bra #main + + main_not_ctx_xfer: + shl b32 $r15 $r14 16 + or $r15 E_BAD_COMMAND + call(error) + bra #main + +// interrupt handler +ih: + push $r0 + push $r8 + mov $r8 $flags + push $r8 + push $r9 + push $r10 + push $r11 + push $r13 + push $r14 + push $r15 + clear b32 $r0 + + // incoming fifo command? + nv_iord($r10, NV_PGRAPH_GPCX_GPCCS_INTR, 0) + and $r11 $r10 NV_PGRAPH_GPCX_GPCCS_INTR_FIFO + bra e #ih_no_fifo + // queue incoming fifo command for later processing + mov $r13 #cmd_queue + nv_iord($r14, NV_PGRAPH_GPCX_GPCCS_FIFO_CMD, 0) + nv_iord($r15, NV_PGRAPH_GPCX_GPCCS_FIFO_DATA, 0) + call(queue_put) + mov $r14 1 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_FIFO_ACK, 0, $r14) + + // ack, and wake up main() + ih_no_fifo: + nv_iowr(NV_PGRAPH_GPCX_GPCCS_INTR_ACK, 0, $r10) + + pop $r15 + pop $r14 + pop $r13 + pop $r11 + pop $r10 + pop $r9 + pop $r8 + mov $flags $r8 + pop $r8 + pop $r0 + bclr $flags $p0 + iret + +// Set this GPC's bit in HUB_BAR, used to signal completion of various +// activities to the HUB fuc +// +hub_barrier_done: + mov $r15 1 + ld b32 $r14 D[$r0 + #gpc_id] + shl b32 $r15 $r14 + nv_wr32(0x409418, $r15) // 0x409418 - HUB_BAR_SET + ret + +// Disables various things, waits a bit, and re-enables them.. +// +// Not sure how exactly this helps, perhaps "ENABLE" is not such a +// good description for the bits we turn off? Anyways, without this, +// funny things happen. +// +ctx_redswitch: + mov $r15 NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_POWER + nv_iowr(NV_PGRAPH_GPCX_GPCCS_RED_SWITCH, 0, $r15) + mov $r14 8 + ctx_redswitch_delay: + sub b32 $r14 1 + bra ne #ctx_redswitch_delay + or $r15 NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_UNK11 + or $r15 NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_ENABLE + nv_iowr(NV_PGRAPH_GPCX_GPCCS_RED_SWITCH, 0, $r15) + ret + +// Transfer GPC context data between GPU and storage area +// +// In: $r15 context base address +// $p1 clear on save, set on load +// $p2 set if opposite direction done/will be done, so: +// on save it means: "a load will follow this save" +// on load it means: "a save preceeded this load" +// +ctx_xfer: + // set context base address + nv_iowr(NV_PGRAPH_GPCX_GPCCS_MEM_BASE, 0, $r15) +#if CHIPSET >= GM107 + gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_MEM_BASE, $r15) +#endif + bra not $p1 #ctx_xfer_not_load + call(ctx_redswitch) + ctx_xfer_not_load: + + // strands + call(strand_pre) + clear b32 $r2 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_STRAND_SELECT, 0x3f, $r2) + xbit $r2 $flags $p1 // SAVE/LOAD + add b32 $r2 NV_PGRAPH_GPCX_GPCCS_STRAND_CMD_SAVE + nv_iowr(NV_PGRAPH_GPCX_GPCCS_STRAND_CMD, 0x3f, $r2) + +#if CHIPSET >= GM107 + tpc_strand_enable(); + tpc_strand_seek(0); + xbit $r15 $flags $p1 // SAVE/LOAD + add b32 $r15 NV_PGRAPH_GPC0_TPCX_STRAND_CMD_SAVE + gpc_wr32(NV_PGRAPH_GPC0_TPCX_STRAND_CMD, $r15) +#endif + + // mmio context + xbit $r10 $flags $p1 // direction + or $r10 2 // first + imm32($r11,0x500000) + ld b32 $r12 D[$r0 + #gpc_id] + shl b32 $r12 15 + add b32 $r11 $r12 // base = NV_PGRAPH_GPCn + ld b32 $r12 D[$r0 + #gpc_mmio_list_head] + ld b32 $r13 D[$r0 + #gpc_mmio_list_tail] + mov $r14 0 // not multi + call(mmctx_xfer) + + // per-TPC mmio context + xbit $r10 $flags $p1 // direction +#if !NV_PGRAPH_GPCX_UNK__SIZE + or $r10 4 // last +#endif + imm32($r11, 0x504000) + ld b32 $r12 D[$r0 + #gpc_id] + shl b32 $r12 15 + add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0 + ld b32 $r12 D[$r0 + #tpc_mmio_list_head] + ld b32 $r13 D[$r0 + #tpc_mmio_list_tail] + ld b32 $r15 D[$r0 + #tpc_mask] + mov $r14 0x800 // stride = 0x800 + call(mmctx_xfer) + +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 + // per-UNK mmio context + xbit $r10 $flags $p1 // direction + or $r10 4 // last + imm32($r11, 0x503000) + ld b32 $r12 D[$r0 + #gpc_id] + shl b32 $r12 15 + add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_UNK0 + ld b32 $r12 D[$r0 + #unk_mmio_list_head] + ld b32 $r13 D[$r0 + #unk_mmio_list_tail] + ld b32 $r15 D[$r0 + #unk_mask] + mov $r14 0x200 // stride = 0x200 + call(mmctx_xfer) +#endif + + // wait for strands to finish + call(strand_wait) +#if CHIPSET >= GM107 + tpc_strand_wait() +#endif + + // if load, or a save without a load following, do some + // unknown stuff that's done after finishing a block of + // strand commands + bra $p1 #ctx_xfer_post + bra not $p2 #ctx_xfer_done + ctx_xfer_post: + call(strand_post) +#if CHIPSET >= GM107 + tpc_strand_disable() +#endif + + // mark completion in HUB's barrier + ctx_xfer_done: + call(hub_barrier_done) + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 new file mode 100644 index 000000000..7cf2bf9d9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3 @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000000 + +#define CHIPSET GF100 +#include "macros.fuc" + +.section #gf100_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #gf100_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h new file mode 100644 index 000000000..54e14b4d3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf100.fuc3.h @@ -0,0 +1,532 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gf100_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x00000064, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x00000064, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x00000064, +/* 0x000c: unk_mmio_list_tail */ + 0x00000064, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gf100_grgpc_code[] = { + 0x03a10ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0xe0f900f8, + 0xf102ffb9, + 0xf09814e7, + 0x21f440e3, + 0x01f7f09d, + 0xf102ffb9, + 0xf09c1ce7, + 0x21f440e3, + 0xf8e0fc9d, +/* 0x03a1: init */ + 0xf104bd00, + 0xf0420017, + 0x11cf0013, + 0x0911e700, + 0x0814b601, + 0xf00014fe, + 0x07f10227, + 0x03f01200, + 0x0002d000, + 0x17f104bd, + 0x10fe04f8, + 0x0007f100, + 0x0003f007, + 0xbd0000d0, + 0x0427f004, + 0x040007f1, + 0xd00003f0, + 0x04bd0002, + 0xf11031f4, + 0xf0820027, + 0x22cf0123, + 0x0137f000, + 0xbb1f24f0, + 0x32b60432, + 0x05028001, + 0xf1060380, + 0xf0860027, + 0x22cf0123, + 0x04028000, + 0xf10f24b6, + 0xf0c90007, + 0x02d00103, + 0xf104bd00, + 0xf0010027, + 0x22cf0223, + 0x9534bd00, + 0x07f10825, + 0x03f0c000, + 0x0005d001, + 0x07f104bd, + 0x03f0c100, + 0x0005d001, + 0x0e9804bd, + 0x010f9800, + 0x015021f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x015021f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0xf10235b6, + 0xf0d30007, + 0x03d00103, + 0xb604bd00, + 0x35b60825, + 0x0120b606, + 0xb60130b6, + 0x34b60824, + 0x022fb908, + 0x02d321f5, + 0xbb002fbb, + 0x07f1003f, + 0x03f00100, + 0x0003d002, + 0x24bd04bd, + 0xf11f29f0, + 0xf0080007, + 0x02d00203, +/* 0x04bb: wait */ + 0xf404bd00, + 0x31f40028, +/* 0x04c1: main */ + 0x1cd7f000, + 0xf43921f4, + 0xe4b0f401, + 0x1e18f404, + 0xf00181fe, + 0x20bd0627, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x05b421f5, +/* 0x04eb: main_not_ctx_xfer */ + 0x94d90ef4, + 0xf5f010ef, + 0x7e21f501, + 0xcc0ef403, +/* 0x04f8: ih */ + 0x80f900f9, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf02c0bf4, + 0xe7f11cd7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x0548: ih_no_fifo */ + 0x010007f1, + 0xd00003f0, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xfc80fc00, + 0x0032f400, +/* 0x056e: hub_barrier_done */ + 0xf7f001f8, + 0x040e9801, + 0xb904febb, + 0xe7f102ff, + 0xe3f09418, + 0x9d21f440, +/* 0x0586: ctx_redswitch */ + 0xf7f000f8, + 0x0007f120, + 0x0103f085, + 0xbd000fd0, + 0x08e7f004, +/* 0x0598: ctx_redswitch_delay */ + 0xf401e2b6, + 0xf5f1fd1b, + 0xf5f10800, + 0x07f10200, + 0x03f08500, + 0x000fd001, + 0x00f804bd, +/* 0x05b4: ctx_xfer */ + 0x810007f1, + 0xd00203f0, + 0x04bd000f, + 0xf50711f4, +/* 0x05c7: ctx_xfer_not_load */ + 0xf5058621, + 0xbd026a21, + 0xfc07f124, + 0x0203f047, + 0xbd0002d0, + 0x012cf004, + 0xf10320b6, + 0xf04afc07, + 0x02d00203, + 0xf004bd00, + 0xa5f001ac, + 0x00b7f102, + 0x50b3f000, + 0xb6040c98, + 0xbcbb0fc4, + 0x000c9800, + 0xf0010d98, + 0x21f500e7, + 0xacf0016f, + 0x04a5f001, + 0x4000b7f1, + 0x9850b3f0, + 0xc4b6040c, + 0x00bcbb0f, + 0x98010c98, + 0x0f98020d, + 0x00e7f106, + 0x6f21f508, + 0x5e21f501, + 0x0601f402, +/* 0x063f: ctx_xfer_post */ + 0xf50712f4, +/* 0x0643: ctx_xfer_done */ + 0xf5027f21, + 0xf8056e21, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 new file mode 100644 index 000000000..c918f7d60 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3 @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000001 + +#define CHIPSET GF117 +#include "macros.fuc" + +.section #gf117_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #gf117_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h new file mode 100644 index 000000000..67524e615 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h @@ -0,0 +1,539 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gf117_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000000, +/* 0x0020: unk_mask */ + 0x00000000, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gf117_grgpc_code[] = { + 0x03a10ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0xe0f900f8, + 0xf102ffb9, + 0xf09814e7, + 0x21f440e3, + 0x01f7f09d, + 0xf102ffb9, + 0xf09c1ce7, + 0x21f440e3, + 0xf8e0fc9d, +/* 0x03a1: init */ + 0xf104bd00, + 0xf0420017, + 0x11cf0013, + 0x0911e700, + 0x0814b601, + 0xf00014fe, + 0x07f10227, + 0x03f01200, + 0x0002d000, + 0x17f104bd, + 0x10fe0545, + 0x0007f100, + 0x0003f007, + 0xbd0000d0, + 0x0427f004, + 0x040007f1, + 0xd00003f0, + 0x04bd0002, + 0xf11031f4, + 0xf0820027, + 0x22cf0123, + 0x0137f000, + 0xbb1f24f0, + 0x32b60432, + 0x05028001, + 0xf1060380, + 0xf0860027, + 0x22cf0123, + 0x04028000, + 0xf10f24b6, + 0xf0c90007, + 0x02d00103, + 0xf104bd00, + 0xf00c30e7, + 0xe5f050e3, + 0xbd24bd01, +/* 0x0433: init_unk_loop */ + 0xf444bd34, + 0xf6b06821, + 0x0f0bf400, + 0xbb01f7f0, + 0x4ffd04f2, + 0x0130b605, +/* 0x0448: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf401, +/* 0x0454: init_unk_done */ + 0x80070380, + 0x27f10804, + 0x23f00100, + 0x0022cf02, + 0x259534bd, + 0x0007f108, + 0x0103f0c0, + 0xbd0005d0, + 0x0007f104, + 0x0103f0c1, + 0xbd0005d0, + 0x000e9804, + 0xf5010f98, + 0xbb015021, + 0x3fbb002f, + 0x010e9800, + 0xf5020f98, + 0x98015021, + 0xeffd050e, + 0x002ebb00, + 0x98003ebb, + 0x0f98020e, + 0x5021f503, + 0x070e9801, + 0xbb00effd, + 0x3ebb002e, + 0x0235b600, + 0xd30007f1, + 0xd00103f0, + 0x04bd0003, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb90834, + 0xd321f502, + 0x002fbb02, + 0xf1003fbb, + 0xf0010007, + 0x03d00203, + 0xbd04bd00, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, +/* 0x0508: wait */ + 0xf40028f4, +/* 0x050e: main */ + 0xd7f00031, + 0x3921f424, + 0xb0f401f4, + 0x18f404e4, + 0x0181fe1e, + 0xbd0627f0, + 0x0412fd20, + 0xfd01e4b6, + 0x18fe051e, + 0x0121f500, + 0xd90ef406, +/* 0x0538: main_not_ctx_xfer */ + 0xf010ef94, + 0x21f501f5, + 0x0ef4037e, +/* 0x0545: ih */ + 0xf900f9cc, + 0x0188fe80, + 0x90f980f9, + 0xb0f9a0f9, + 0xe0f9d0f9, + 0x04bdf0f9, + 0x0200a7f1, + 0xcf00a3f0, + 0xabc400aa, + 0x2c0bf404, + 0xf124d7f0, + 0xf01a00e7, + 0xeecf00e3, + 0x00f7f100, + 0x00f3f019, + 0xf400ffcf, + 0xe7f00421, + 0x0007f101, + 0x0003f01d, + 0xbd000ed0, +/* 0x0595: ih_no_fifo */ + 0x0007f104, + 0x0003f001, + 0xbd000ad0, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0x32f400fc, +/* 0x05bb: hub_barrier_done */ + 0xf001f800, + 0x0e9801f7, + 0x04febb04, + 0xf102ffb9, + 0xf09418e7, + 0x21f440e3, +/* 0x05d3: ctx_redswitch */ + 0xf000f89d, + 0x07f120f7, + 0x03f08500, + 0x000fd001, + 0xe7f004bd, +/* 0x05e5: ctx_redswitch_delay */ + 0x01e2b608, + 0xf1fd1bf4, + 0xf10800f5, + 0xf10200f5, + 0xf0850007, + 0x0fd00103, + 0xf804bd00, +/* 0x0601: ctx_xfer */ + 0x0007f100, + 0x0203f081, + 0xbd000fd0, + 0x0711f404, + 0x05d321f5, +/* 0x0614: ctx_xfer_not_load */ + 0x026a21f5, + 0x07f124bd, + 0x03f047fc, + 0x0002d002, + 0x2cf004bd, + 0x0320b601, + 0x4afc07f1, + 0xd00203f0, + 0x04bd0002, + 0xf001acf0, + 0xb7f102a5, + 0xb3f00000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x010d9800, + 0xf500e7f0, + 0xf0016f21, + 0xb7f101ac, + 0xb3f04000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x020d9801, + 0xf1060f98, + 0xf50800e7, + 0xf0016f21, + 0xa5f001ac, + 0x00b7f104, + 0x50b3f030, + 0xb6040c98, + 0xbcbb0fc4, + 0x020c9800, + 0x98030d98, + 0xe7f1080f, + 0x21f50200, + 0x21f5016f, + 0x01f4025e, + 0x0712f406, +/* 0x06b0: ctx_xfer_post */ + 0x027f21f5, +/* 0x06b4: ctx_xfer_done */ + 0x05bb21f5, + 0x000000f8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 new file mode 100644 index 000000000..b80cdfd33 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3 @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000001 + +#define CHIPSET GK100 +#include "macros.fuc" + +.section #gk104_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #gk104_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h new file mode 100644 index 000000000..60c8b7e89 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h @@ -0,0 +1,539 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gk104_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000000, +/* 0x0020: unk_mask */ + 0x00000000, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gk104_grgpc_code[] = { + 0x03a10ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0xe0f900f8, + 0xf102ffb9, + 0xf09814e7, + 0x21f440e3, + 0x01f7f09d, + 0xf102ffb9, + 0xf09c1ce7, + 0x21f440e3, + 0xf8e0fc9d, +/* 0x03a1: init */ + 0xf104bd00, + 0xf0420017, + 0x11cf0013, + 0x0911e700, + 0x0814b601, + 0xf00014fe, + 0x07f10227, + 0x03f01200, + 0x0002d000, + 0x17f104bd, + 0x10fe0545, + 0x0007f100, + 0x0003f007, + 0xbd0000d0, + 0x0427f004, + 0x040007f1, + 0xd00003f0, + 0x04bd0002, + 0xf11031f4, + 0xf0820027, + 0x22cf0123, + 0x0137f000, + 0xbb1f24f0, + 0x32b60432, + 0x05028001, + 0xf1060380, + 0xf0860027, + 0x22cf0123, + 0x04028000, + 0xf10f24b6, + 0xf0c90007, + 0x02d00103, + 0xf104bd00, + 0xf00c30e7, + 0xe5f050e3, + 0xbd24bd01, +/* 0x0433: init_unk_loop */ + 0xf444bd34, + 0xf6b06821, + 0x0f0bf400, + 0xbb01f7f0, + 0x4ffd04f2, + 0x0130b605, +/* 0x0448: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf401, +/* 0x0454: init_unk_done */ + 0x80070380, + 0x27f10804, + 0x23f00100, + 0x0022cf02, + 0x259534bd, + 0x0007f108, + 0x0103f0c0, + 0xbd0005d0, + 0x0007f104, + 0x0103f0c1, + 0xbd0005d0, + 0x000e9804, + 0xf5010f98, + 0xbb015021, + 0x3fbb002f, + 0x010e9800, + 0xf5020f98, + 0x98015021, + 0xeffd050e, + 0x002ebb00, + 0x98003ebb, + 0x0f98020e, + 0x5021f503, + 0x070e9801, + 0xbb00effd, + 0x3ebb002e, + 0x0235b600, + 0xd30007f1, + 0xd00103f0, + 0x04bd0003, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb90834, + 0xd321f502, + 0x002fbb02, + 0xf1003fbb, + 0xf0010007, + 0x03d00203, + 0xbd04bd00, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, +/* 0x0508: wait */ + 0xf40028f4, +/* 0x050e: main */ + 0xd7f00031, + 0x3921f424, + 0xb0f401f4, + 0x18f404e4, + 0x0181fe1e, + 0xbd0627f0, + 0x0412fd20, + 0xfd01e4b6, + 0x18fe051e, + 0x0121f500, + 0xd90ef406, +/* 0x0538: main_not_ctx_xfer */ + 0xf010ef94, + 0x21f501f5, + 0x0ef4037e, +/* 0x0545: ih */ + 0xf900f9cc, + 0x0188fe80, + 0x90f980f9, + 0xb0f9a0f9, + 0xe0f9d0f9, + 0x04bdf0f9, + 0x0200a7f1, + 0xcf00a3f0, + 0xabc400aa, + 0x2c0bf404, + 0xf124d7f0, + 0xf01a00e7, + 0xeecf00e3, + 0x00f7f100, + 0x00f3f019, + 0xf400ffcf, + 0xe7f00421, + 0x0007f101, + 0x0003f01d, + 0xbd000ed0, +/* 0x0595: ih_no_fifo */ + 0x0007f104, + 0x0003f001, + 0xbd000ad0, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0x32f400fc, +/* 0x05bb: hub_barrier_done */ + 0xf001f800, + 0x0e9801f7, + 0x04febb04, + 0xf102ffb9, + 0xf09418e7, + 0x21f440e3, +/* 0x05d3: ctx_redswitch */ + 0xf000f89d, + 0x07f120f7, + 0x03f08500, + 0x000fd001, + 0xe7f004bd, +/* 0x05e5: ctx_redswitch_delay */ + 0x01e2b608, + 0xf1fd1bf4, + 0xf10800f5, + 0xf10200f5, + 0xf0850007, + 0x0fd00103, + 0xf804bd00, +/* 0x0601: ctx_xfer */ + 0x0007f100, + 0x0203f081, + 0xbd000fd0, + 0x0711f404, + 0x05d321f5, +/* 0x0614: ctx_xfer_not_load */ + 0x026a21f5, + 0x07f124bd, + 0x03f047fc, + 0x0002d002, + 0x2cf004bd, + 0x0320b601, + 0x4afc07f1, + 0xd00203f0, + 0x04bd0002, + 0xf001acf0, + 0xb7f102a5, + 0xb3f00000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x010d9800, + 0xf500e7f0, + 0xf0016f21, + 0xb7f101ac, + 0xb3f04000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x020d9801, + 0xf1060f98, + 0xf50800e7, + 0xf0016f21, + 0xa5f001ac, + 0x00b7f104, + 0x50b3f030, + 0xb6040c98, + 0xbcbb0fc4, + 0x020c9800, + 0x98030d98, + 0xe7f1080f, + 0x21f50200, + 0x21f5016f, + 0x01f4025e, + 0x0712f406, +/* 0x06b0: ctx_xfer_post */ + 0x027f21f5, +/* 0x06b4: ctx_xfer_done */ + 0x05bb21f5, + 0x000000f8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 new file mode 100644 index 000000000..98d85fe21 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3 @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000002 + +#define CHIPSET GK110 +#include "macros.fuc" + +.section #gk110_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #gk110_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h new file mode 100644 index 000000000..c99d15665 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h @@ -0,0 +1,539 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gk110_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000000, +/* 0x0020: unk_mask */ + 0x00000000, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gk110_grgpc_code[] = { + 0x03a10ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f037, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f037, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0xe0f900f8, + 0xf102ffb9, + 0xf09814e7, + 0x21f440e3, + 0x01f7f09d, + 0xf102ffb9, + 0xf09c1ce7, + 0x21f440e3, + 0xf8e0fc9d, +/* 0x03a1: init */ + 0xf104bd00, + 0xf0420017, + 0x11cf0013, + 0x0911e700, + 0x0814b601, + 0xf00014fe, + 0x07f10227, + 0x03f01200, + 0x0002d000, + 0x17f104bd, + 0x10fe0545, + 0x0007f100, + 0x0003f007, + 0xbd0000d0, + 0x0427f004, + 0x040007f1, + 0xd00003f0, + 0x04bd0002, + 0xf11031f4, + 0xf0820027, + 0x22cf0123, + 0x0137f000, + 0xbb1f24f0, + 0x32b60432, + 0x05028001, + 0xf1060380, + 0xf0860027, + 0x22cf0123, + 0x04028000, + 0xf10f24b6, + 0xf0c90007, + 0x02d00103, + 0xf104bd00, + 0xf00c30e7, + 0xe5f050e3, + 0xbd24bd01, +/* 0x0433: init_unk_loop */ + 0xf444bd34, + 0xf6b06821, + 0x0f0bf400, + 0xbb01f7f0, + 0x4ffd04f2, + 0x0130b605, +/* 0x0448: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf402, +/* 0x0454: init_unk_done */ + 0x80070380, + 0x27f10804, + 0x23f00100, + 0x0022cf02, + 0x259534bd, + 0x0007f108, + 0x0103f0c0, + 0xbd0005d0, + 0x0007f104, + 0x0103f0c1, + 0xbd0005d0, + 0x000e9804, + 0xf5010f98, + 0xbb015021, + 0x3fbb002f, + 0x010e9800, + 0xf5020f98, + 0x98015021, + 0xeffd050e, + 0x002ebb00, + 0x98003ebb, + 0x0f98020e, + 0x5021f503, + 0x070e9801, + 0xbb00effd, + 0x3ebb002e, + 0x0235b600, + 0xd30007f1, + 0xd00103f0, + 0x04bd0003, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb90834, + 0xd321f502, + 0x002fbb02, + 0xf1003fbb, + 0xf0010007, + 0x03d00203, + 0xbd04bd00, + 0x1f29f024, + 0x300007f1, + 0xd00203f0, + 0x04bd0002, +/* 0x0508: wait */ + 0xf40028f4, +/* 0x050e: main */ + 0xd7f00031, + 0x3921f424, + 0xb0f401f4, + 0x18f404e4, + 0x0181fe1e, + 0xbd0627f0, + 0x0412fd20, + 0xfd01e4b6, + 0x18fe051e, + 0x0121f500, + 0xd90ef406, +/* 0x0538: main_not_ctx_xfer */ + 0xf010ef94, + 0x21f501f5, + 0x0ef4037e, +/* 0x0545: ih */ + 0xf900f9cc, + 0x0188fe80, + 0x90f980f9, + 0xb0f9a0f9, + 0xe0f9d0f9, + 0x04bdf0f9, + 0x0200a7f1, + 0xcf00a3f0, + 0xabc400aa, + 0x2c0bf404, + 0xf124d7f0, + 0xf01a00e7, + 0xeecf00e3, + 0x00f7f100, + 0x00f3f019, + 0xf400ffcf, + 0xe7f00421, + 0x0007f101, + 0x0003f01d, + 0xbd000ed0, +/* 0x0595: ih_no_fifo */ + 0x0007f104, + 0x0003f001, + 0xbd000ad0, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0x32f400fc, +/* 0x05bb: hub_barrier_done */ + 0xf001f800, + 0x0e9801f7, + 0x04febb04, + 0xf102ffb9, + 0xf09418e7, + 0x21f440e3, +/* 0x05d3: ctx_redswitch */ + 0xf000f89d, + 0x07f120f7, + 0x03f08500, + 0x000fd001, + 0xe7f004bd, +/* 0x05e5: ctx_redswitch_delay */ + 0x01e2b608, + 0xf1fd1bf4, + 0xf10800f5, + 0xf10200f5, + 0xf0850007, + 0x0fd00103, + 0xf804bd00, +/* 0x0601: ctx_xfer */ + 0x0007f100, + 0x0203f081, + 0xbd000fd0, + 0x0711f404, + 0x05d321f5, +/* 0x0614: ctx_xfer_not_load */ + 0x026a21f5, + 0x07f124bd, + 0x03f047fc, + 0x0002d002, + 0x2cf004bd, + 0x0320b601, + 0x4afc07f1, + 0xd00203f0, + 0x04bd0002, + 0xf001acf0, + 0xb7f102a5, + 0xb3f00000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x010d9800, + 0xf500e7f0, + 0xf0016f21, + 0xb7f101ac, + 0xb3f04000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x020d9801, + 0xf1060f98, + 0xf50800e7, + 0xf0016f21, + 0xa5f001ac, + 0x00b7f104, + 0x50b3f030, + 0xb6040c98, + 0xbcbb0fc4, + 0x020c9800, + 0x98030d98, + 0xe7f1080f, + 0x21f50200, + 0x21f5016f, + 0x01f4025e, + 0x0712f406, +/* 0x06b0: ctx_xfer_post */ + 0x027f21f5, +/* 0x06b4: ctx_xfer_done */ + 0x05bb21f5, + 0x000000f8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 new file mode 100644 index 000000000..8f64299a3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5 @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000001 + +#define CHIPSET GK208 +#include "macros.fuc" + +.section #gk208_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #gk208_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h new file mode 100644 index 000000000..753aa6672 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h @@ -0,0 +1,475 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gk208_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000000, +/* 0x0020: unk_mask */ + 0x00000000, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gk208_grgpc_code[] = { + 0x03140ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0xf489a408, + 0x020f0b1b, + 0x0002f87e, +/* 0x001a: queue_put_next */ + 0x98c400f8, + 0x0384b607, + 0xb6008dbb, + 0x8eb50880, + 0x018fb500, + 0xf00190b6, + 0xd9b50f94, +/* 0x0037: queue_get */ + 0xf400f801, + 0xd8980131, + 0x01d99800, + 0x0bf489a4, + 0x0789c421, + 0xbb0394b6, + 0x90b6009d, + 0x009e9808, + 0xb6019f98, + 0x84f00180, + 0x00d8b50f, +/* 0x0063: queue_get_done */ + 0xf80132f4, +/* 0x0065: nv_rd32 */ + 0xf0ecb200, + 0x00801fc9, + 0x0cf601ca, +/* 0x0073: nv_rd32_wait */ + 0x8c04bd00, + 0xcf01ca00, + 0xccc800cc, + 0xf61bf41f, + 0xec7e060a, + 0x008f0000, + 0xffcf01cb, +/* 0x008f: nv_wr32 */ + 0x8000f800, + 0xf601cc00, + 0x04bd000f, + 0xc9f0ecb2, + 0x1ec9f01f, + 0x01ca0080, + 0xbd000cf6, +/* 0x00a9: nv_wr32_wait */ + 0xca008c04, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f61b, +/* 0x00b8: wait_donez */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x00cf: wait_donez_ne */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf61bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x00ec: wait_doneo */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x0103: wait_doneo_e */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf60bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x0120: mmctx_size */ +/* 0x0122: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0x1bf4efa4, + 0xf89fb2ec, +/* 0x013d: mmctx_xfer */ + 0xf094bd00, + 0x00800199, + 0x09f60237, + 0xbd04bd00, + 0x05bbfd94, + 0x800f0bf4, + 0xf601c400, + 0x04bd000b, +/* 0x015f: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0xc6008018, + 0x000ef601, + 0x008004bd, + 0x0ff601c7, + 0xf004bd00, +/* 0x017a: mmctx_multi_disabled */ + 0xabc80199, + 0x10b4b600, + 0xc80cb9f0, + 0xe4b601ae, + 0x05befd11, + 0x01c50080, + 0xbd000bf6, +/* 0x0195: mmctx_exec_loop */ +/* 0x0195: mmctx_wait_free */ + 0xc5008e04, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f60b, + 0x05e9fd00, + 0x01c80080, + 0xbd000ef6, + 0x04c0b604, + 0x1bf4cda4, + 0x02abc8df, +/* 0x01bf: mmctx_fini_wait */ + 0x8b1c1bf4, + 0xcf01c500, + 0xb4f000bb, + 0x10b4b01f, + 0x0af31bf4, + 0x00b87e05, + 0x250ef400, +/* 0x01d8: mmctx_stop */ + 0xb600abc8, + 0xb9f010b4, + 0x12b9f00c, + 0x01c50080, + 0xbd000bf6, +/* 0x01ed: mmctx_stop_wait */ + 0xc5008b04, + 0x00bbcf01, + 0xf412bbc8, +/* 0x01fa: mmctx_done */ + 0x94bdf61b, + 0x800199f0, + 0xf6021700, + 0x04bd0009, +/* 0x020a: strand_wait */ + 0xa0f900f8, + 0xb87e020a, + 0xa0fc0000, +/* 0x0216: strand_pre */ + 0x0c0900f8, + 0x024afc80, + 0xbd0009f6, + 0x020a7e04, +/* 0x0227: strand_post */ + 0x0900f800, + 0x4afc800d, + 0x0009f602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0238: strand_set */ + 0xfc800f0c, + 0x0cf6024f, + 0x0c04bd00, + 0x4afc800b, + 0x000cf602, + 0xfc8004bd, + 0x0ef6024f, + 0x0c04bd00, + 0x4afc800a, + 0x000cf602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0268: strand_ctx_init */ + 0x99f094bd, + 0x37008003, + 0x0009f602, + 0x167e04bd, + 0x030e0002, + 0x0002387e, + 0xfc80c4bd, + 0x0cf60247, + 0x0c04bd00, + 0x4afc8001, + 0x000cf602, + 0x0a7e04bd, + 0x0c920002, + 0x46fc8001, + 0x000cf602, + 0x020c04bd, + 0x024afc80, + 0xbd000cf6, + 0x020a7e04, + 0x02277e00, + 0x42008800, + 0x20008902, + 0x0099cf02, +/* 0x02c7: ctx_init_strand_loop */ + 0xf608fe95, + 0x8ef6008e, + 0x808acf40, + 0xb606a5b6, + 0xeabb01a0, + 0x0480b600, + 0xf40192b6, + 0xe4b6e81b, + 0xf2efbc08, + 0x99f094bd, + 0x17008003, + 0x0009f602, + 0x00f804bd, +/* 0x02f8: error */ + 0xffb2e0f9, + 0x4098148e, + 0x00008f7e, + 0xffb2010f, + 0x409c1c8e, + 0x00008f7e, + 0x00f8e0fc, +/* 0x0314: init */ + 0x004104bd, + 0x0011cf42, + 0x010911e7, + 0xfe0814b6, + 0x02020014, + 0xf6120040, + 0x04bd0002, + 0xfe048441, + 0x00400010, + 0x0000f607, + 0x040204bd, + 0xf6040040, + 0x04bd0002, + 0x821031f4, + 0xcf018200, + 0x01030022, + 0xbb1f24f0, + 0x32b60432, + 0x0502b501, + 0x820603b5, + 0xcf018600, + 0x02b50022, + 0x0f24b604, + 0x01c90080, + 0xbd0002f6, + 0x0c308e04, + 0x01e5f050, + 0x34bd24bd, +/* 0x0386: init_unk_loop */ + 0x657e44bd, + 0xf6b00000, + 0x0e0bf400, + 0xf2bb010f, + 0x054ffd04, +/* 0x039b: init_unk_next */ + 0xb60130b6, + 0xe0b60120, + 0x0126b004, +/* 0x03a7: init_unk_done */ + 0xb5e21bf4, + 0x04b50703, + 0x01008208, + 0x0022cf02, + 0x259534bd, + 0xc0008008, + 0x0005f601, + 0x008004bd, + 0x05f601c1, + 0x9804bd00, + 0x0f98000e, + 0x01207e01, + 0x002fbb00, + 0x98003fbb, + 0x0f98010e, + 0x01207e02, + 0x050e9800, + 0xbb00effd, + 0x3ebb002e, + 0x020e9800, + 0x7e030f98, + 0x98000120, + 0xeffd070e, + 0x002ebb00, + 0xb6003ebb, + 0x00800235, + 0x03f601d3, + 0xb604bd00, + 0x35b60825, + 0x0120b606, + 0xb60130b6, + 0x34b60824, + 0x7e2fb208, + 0xbb000268, + 0x3fbb002f, + 0x01008000, + 0x0003f602, + 0x24bd04bd, + 0x801f29f0, + 0xf6023000, + 0x04bd0002, +/* 0x0448: wait */ + 0xf40028f4, +/* 0x044e: main */ + 0x240d0031, + 0x0000377e, + 0xb0f401f4, + 0x18f404e4, + 0x0181fe1d, + 0x20bd0602, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x00051f7e, +/* 0x0477: main_not_ctx_xfer */ + 0x94da0ef4, + 0xf5f010ef, + 0x02f87e01, + 0xcd0ef400, +/* 0x0484: ih */ + 0x80f900f9, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0x4a04bdf0, + 0xaacf0200, + 0x04abc400, + 0x0d1f0bf4, + 0x1a004e24, + 0x4f00eecf, + 0xffcf1900, + 0x00047e00, + 0x40010e00, + 0x0ef61d00, +/* 0x04c3: ih_no_fifo */ + 0x4004bd00, + 0x0af60100, + 0xfc04bd00, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x00fc80fc, + 0xf80032f4, +/* 0x04e5: hub_barrier_done */ + 0x98010f01, + 0xfebb040e, + 0x8effb204, + 0x7e409418, + 0xf800008f, +/* 0x04f9: ctx_redswitch */ + 0x80200f00, + 0xf6018500, + 0x04bd000f, +/* 0x0506: ctx_redswitch_delay */ + 0xe2b6080e, + 0xfd1bf401, + 0x0800f5f1, + 0x0200f5f1, + 0x01850080, + 0xbd000ff6, +/* 0x051f: ctx_xfer */ + 0x8000f804, + 0xf6028100, + 0x04bd000f, + 0x7e0711f4, +/* 0x052f: ctx_xfer_not_load */ + 0x7e0004f9, + 0xbd000216, + 0x47fc8024, + 0x0002f602, + 0x2cf004bd, + 0x0320b601, + 0x024afc80, + 0xbd0002f6, + 0x01acf004, + 0x8b02a5f0, + 0x98500000, + 0xc4b6040c, + 0x00bcbb0f, + 0x98000c98, + 0x000e010d, + 0x00013d7e, + 0x8b01acf0, + 0x98504000, + 0xc4b6040c, + 0x00bcbb0f, + 0x98010c98, + 0x0f98020d, + 0x08004e06, + 0x00013d7e, + 0xf001acf0, + 0x008b04a5, + 0x0c985030, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98020c, + 0x080f9803, + 0x7e02004e, + 0x7e00013d, + 0xf400020a, + 0x12f40601, +/* 0x05b9: ctx_xfer_post */ + 0x02277e07, +/* 0x05bd: ctx_xfer_done */ + 0x04e57e00, + 0x0000f800, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 new file mode 100644 index 000000000..47802c7ec --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5 @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000002 + +#define CHIPSET GM107 +#include "macros.fuc" + +.section #gm107_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #gm107_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h new file mode 100644 index 000000000..db8b294ce --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h @@ -0,0 +1,607 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gm107_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000000, +/* 0x0020: unk_mask */ + 0x00000000, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gm107_grgpc_code[] = { + 0x03410ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0xf489a408, + 0x020f0b1b, + 0x0002f87e, +/* 0x001a: queue_put_next */ + 0x98c400f8, + 0x0384b607, + 0xb6008dbb, + 0x8eb50880, + 0x018fb500, + 0xf00190b6, + 0xd9b50f94, +/* 0x0037: queue_get */ + 0xf400f801, + 0xd8980131, + 0x01d99800, + 0x0bf489a4, + 0x0789c421, + 0xbb0394b6, + 0x90b6009d, + 0x009e9808, + 0xb6019f98, + 0x84f00180, + 0x00d8b50f, +/* 0x0063: queue_get_done */ + 0xf80132f4, +/* 0x0065: nv_rd32 */ + 0xf0ecb200, + 0x00801fc9, + 0x0cf601ca, +/* 0x0073: nv_rd32_wait */ + 0x8c04bd00, + 0xcf01ca00, + 0xccc800cc, + 0xf61bf41f, + 0xec7e060a, + 0x008f0000, + 0xffcf01cb, +/* 0x008f: nv_wr32 */ + 0x8000f800, + 0xf601cc00, + 0x04bd000f, + 0xc9f0ecb2, + 0x1ec9f01f, + 0x01ca0080, + 0xbd000cf6, +/* 0x00a9: nv_wr32_wait */ + 0xca008c04, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f61b, +/* 0x00b8: wait_donez */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x00cf: wait_donez_ne */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf61bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x00ec: wait_doneo */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x0103: wait_doneo_e */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf60bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x0120: mmctx_size */ +/* 0x0122: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0x1bf4efa4, + 0xf89fb2ec, +/* 0x013d: mmctx_xfer */ + 0xf094bd00, + 0x00800199, + 0x09f60237, + 0xbd04bd00, + 0x05bbfd94, + 0x800f0bf4, + 0xf601c400, + 0x04bd000b, +/* 0x015f: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0xc6008018, + 0x000ef601, + 0x008004bd, + 0x0ff601c7, + 0xf004bd00, +/* 0x017a: mmctx_multi_disabled */ + 0xabc80199, + 0x10b4b600, + 0xc80cb9f0, + 0xe4b601ae, + 0x05befd11, + 0x01c50080, + 0xbd000bf6, +/* 0x0195: mmctx_exec_loop */ +/* 0x0195: mmctx_wait_free */ + 0xc5008e04, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f60b, + 0x05e9fd00, + 0x01c80080, + 0xbd000ef6, + 0x04c0b604, + 0x1bf4cda4, + 0x02abc8df, +/* 0x01bf: mmctx_fini_wait */ + 0x8b1c1bf4, + 0xcf01c500, + 0xb4f000bb, + 0x10b4b01f, + 0x0af31bf4, + 0x00b87e05, + 0x250ef400, +/* 0x01d8: mmctx_stop */ + 0xb600abc8, + 0xb9f010b4, + 0x12b9f00c, + 0x01c50080, + 0xbd000bf6, +/* 0x01ed: mmctx_stop_wait */ + 0xc5008b04, + 0x00bbcf01, + 0xf412bbc8, +/* 0x01fa: mmctx_done */ + 0x94bdf61b, + 0x800199f0, + 0xf6021700, + 0x04bd0009, +/* 0x020a: strand_wait */ + 0xa0f900f8, + 0xb87e020a, + 0xa0fc0000, +/* 0x0216: strand_pre */ + 0x0c0900f8, + 0x024afc80, + 0xbd0009f6, + 0x020a7e04, +/* 0x0227: strand_post */ + 0x0900f800, + 0x4afc800d, + 0x0009f602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0238: strand_set */ + 0xfc800f0c, + 0x0cf6024f, + 0x0c04bd00, + 0x4afc800b, + 0x000cf602, + 0xfc8004bd, + 0x0ef6024f, + 0x0c04bd00, + 0x4afc800a, + 0x000cf602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0268: strand_ctx_init */ + 0x99f094bd, + 0x37008003, + 0x0009f602, + 0x167e04bd, + 0x030e0002, + 0x0002387e, + 0xfc80c4bd, + 0x0cf60247, + 0x0c04bd00, + 0x4afc8001, + 0x000cf602, + 0x0a7e04bd, + 0x0c920002, + 0x46fc8001, + 0x000cf602, + 0x020c04bd, + 0x024afc80, + 0xbd000cf6, + 0x020a7e04, + 0x02277e00, + 0x42008800, + 0x20008902, + 0x0099cf02, +/* 0x02c7: ctx_init_strand_loop */ + 0xf608fe95, + 0x8ef6008e, + 0x808acf40, + 0xb606a5b6, + 0xeabb01a0, + 0x0480b600, + 0xf40192b6, + 0xe4b6e81b, + 0xf2efbc08, + 0x99f094bd, + 0x17008003, + 0x0009f602, + 0x00f804bd, +/* 0x02f8: error */ + 0xffb2e0f9, + 0x4098148e, + 0x00008f7e, + 0xffb2010f, + 0x409c1c8e, + 0x00008f7e, + 0x00f8e0fc, +/* 0x0314: tpc_strand_wait */ + 0x94bd90f9, + 0x800a99f0, + 0xf6023700, + 0x04bd0009, +/* 0x0324: tpc_strand_busy */ + 0x033f0089, + 0xb30099cf, + 0xbdf90094, + 0x0a99f094, + 0x02170080, + 0xbd0009f6, + 0xf890fc04, +/* 0x0341: init */ + 0x4104bd00, + 0x11cf4200, + 0x0911e700, + 0x0814b601, + 0x020014fe, + 0x12004002, + 0xbd0002f6, + 0x05ad4104, + 0x400010fe, + 0x00f60700, + 0x0204bd00, + 0x04004004, + 0xbd0002f6, + 0x1031f404, + 0x01820082, + 0x030022cf, + 0x1f24f001, + 0xb60432bb, + 0x02b50132, + 0x0603b505, + 0x01860082, + 0xb50022cf, + 0x24b60402, + 0xc900800f, + 0x0002f601, + 0x308e04bd, + 0xe5f0500c, + 0xbd24bd01, +/* 0x03b3: init_unk_loop */ + 0x7e44bd34, + 0xb0000065, + 0x0bf400f6, + 0xbb010f0e, + 0x4ffd04f2, + 0x0130b605, +/* 0x03c8: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf402, +/* 0x03d4: init_unk_done */ + 0xb50703b5, + 0x00820804, + 0x22cf0201, + 0x9534bd00, + 0x00800825, + 0x05f601c0, + 0x8004bd00, + 0xf601c100, + 0x04bd0005, + 0x98000e98, + 0x207e010f, + 0x2fbb0001, + 0x003fbb00, + 0x98010e98, + 0x207e020f, + 0x0e980001, + 0x00effd05, + 0xbb002ebb, + 0x0e98003e, + 0x030f9802, + 0x0001207e, + 0xfd070e98, + 0x2ebb00ef, + 0x003ebb00, + 0x800235b6, + 0xf601d300, + 0x04bd0003, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb20834, + 0x0002687e, + 0xbb002fbb, + 0x3f0f003f, + 0x501d608e, + 0xb201e5f0, + 0x008f7eff, + 0x8e0c0f00, + 0xf0501da8, + 0xffb201e5, + 0x00008f7e, + 0x0003147e, + 0x608e3f0f, + 0xe5f0501d, + 0x7effb201, + 0x0f00008f, + 0x1d9c8e00, + 0x01e5f050, + 0x8f7effb2, + 0x010f0000, + 0x0003147e, + 0x501da88e, + 0xb201e5f0, + 0x008f7eff, + 0x8eff0f00, + 0xf0501d98, + 0xffb201e5, + 0x00008f7e, + 0xa88e020f, + 0xe5f0501d, + 0x7effb201, + 0x7e00008f, + 0x98000314, + 0x00850504, + 0x55f05040, +/* 0x04dd: tpc_strand_init_tpc_loop */ + 0x705eb801, + 0x657e0005, + 0xf6b20000, +/* 0x04ea: tpc_strand_init_idx_loop */ + 0x5eb874bd, + 0xb2000560, + 0x008f7e7f, + 0x885eb800, + 0x2f950005, + 0x008f7e08, + 0x8c5eb800, + 0x2f950005, + 0x008f7e08, + 0x905eb800, + 0x657e0005, + 0xf5b60000, + 0x01f0b606, + 0xbb08f4b6, + 0x3fbb002f, + 0x0170b600, + 0xf40162b6, + 0x50b7bf1b, + 0x42b60800, + 0xa81bf401, + 0x608e3f0f, + 0xe5f0501d, + 0x7effb201, + 0x0f00008f, + 0x1da88e0d, + 0x01e5f050, + 0x8f7effb2, + 0x147e0000, + 0x00800003, + 0x03f60201, + 0xbd04bd00, + 0x1f29f024, + 0x02300080, + 0xbd0002f6, +/* 0x0571: wait */ + 0x0028f404, +/* 0x0577: main */ + 0x0d0031f4, + 0x00377e24, + 0xf401f400, + 0xf404e4b0, + 0x81fe1d18, + 0xbd060201, + 0x0412fd20, + 0xfd01e4b6, + 0x18fe051e, + 0x06487e00, + 0xda0ef400, +/* 0x05a0: main_not_ctx_xfer */ + 0xf010ef94, + 0xf87e01f5, + 0x0ef40002, +/* 0x05ad: ih */ + 0xf900f9cd, + 0x0188fe80, + 0x90f980f9, + 0xb0f9a0f9, + 0xe0f9d0f9, + 0x04bdf0f9, + 0xcf02004a, + 0xabc400aa, + 0x1f0bf404, + 0x004e240d, + 0x00eecf1a, + 0xcf19004f, + 0x047e00ff, + 0x010e0000, + 0xf61d0040, + 0x04bd000e, +/* 0x05ec: ih_no_fifo */ + 0xf6010040, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xfc80fc00, + 0x0032f400, +/* 0x060e: hub_barrier_done */ + 0x010f01f8, + 0xbb040e98, + 0xffb204fe, + 0x4094188e, + 0x00008f7e, +/* 0x0622: ctx_redswitch */ + 0x200f00f8, + 0x01850080, + 0xbd000ff6, +/* 0x062f: ctx_redswitch_delay */ + 0xb6080e04, + 0x1bf401e2, + 0x00f5f1fd, + 0x00f5f108, + 0x85008002, + 0x000ff601, + 0x00f804bd, +/* 0x0648: ctx_xfer */ + 0x02810080, + 0xbd000ff6, + 0x1dc48e04, + 0x01e5f050, + 0x8f7effb2, + 0x11f40000, + 0x06227e07, +/* 0x0665: ctx_xfer_not_load */ + 0x02167e00, + 0x8024bd00, + 0xf60247fc, + 0x04bd0002, + 0xb6012cf0, + 0xfc800320, + 0x02f6024a, + 0x0f04bd00, + 0x1da88e0c, + 0x01e5f050, + 0x8f7effb2, + 0x147e0000, + 0x3f0f0003, + 0x501d608e, + 0xb201e5f0, + 0x008f7eff, + 0x8e000f00, + 0xf0501d9c, + 0xffb201e5, + 0x00008f7e, + 0x147e010f, + 0xfcf00003, + 0x03f0b601, + 0x501da88e, + 0xb201e5f0, + 0x008f7eff, + 0x01acf000, + 0x8b02a5f0, + 0x98500000, + 0xc4b6040c, + 0x00bcbb0f, + 0x98000c98, + 0x000e010d, + 0x00013d7e, + 0x8b01acf0, + 0x98504000, + 0xc4b6040c, + 0x00bcbb0f, + 0x98010c98, + 0x0f98020d, + 0x08004e06, + 0x00013d7e, + 0xf001acf0, + 0x008b04a5, + 0x0c985030, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98020c, + 0x080f9803, + 0x7e02004e, + 0x7e00013d, + 0x7e00020a, + 0xf4000314, + 0x12f40601, +/* 0x073d: ctx_xfer_post */ + 0x02277e1a, + 0x8e0d0f00, + 0xf0501da8, + 0xffb201e5, + 0x00008f7e, + 0x0003147e, +/* 0x0754: ctx_xfer_done */ + 0x00060e7e, + 0x000000f8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc new file mode 100644 index 000000000..4d416d4f8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hub.fuc @@ -0,0 +1,699 @@ +/* fuc microcode for gf100 PGRAPH/HUB + * + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#ifdef INCLUDE_DATA +hub_mmio_list_head: .b32 #hub_mmio_list_base +hub_mmio_list_tail: .b32 #hub_mmio_list_next + +gpc_count: .b32 0 +rop_count: .b32 0 +cmd_queue: queue_init + +ctx_current: .b32 0 + +.align 256 +chan_data: +chan_mmio_count: .b32 0 +chan_mmio_address: .b32 0 + +.align 256 +xfer_data: .skip 256 + +hub_mmio_list_base: +.b32 0x0417e91c // 0x17e91c, 2 +hub_mmio_list_next: +#endif + +#ifdef INCLUDE_CODE +// reports an exception to the host +// +// In: $r15 error code (see os.h) +// +error: + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(5), 0, $r15) + mov $r15 1 + nv_iowr(NV_PGRAPH_FECS_INTR_UP_SET, 0, $r15) + ret + +// HUB fuc initialisation, executed by triggering ucode start, will +// fall through to main loop after completion. +// +// Output: +// CC_SCRATCH[0]: +// 31:31: set to signal completion +// CC_SCRATCH[1]: +// 31:0: total PGRAPH context size +// +init: + clear b32 $r0 + mov $xdbase $r0 + + // setup stack + nv_iord($r1, NV_PGRAPH_FECS_CAPS, 0) + extr $r1 $r1 9:17 + shl b32 $r1 8 + mov $sp $r1 + + // enable fifo access + mov $r2 NV_PGRAPH_FECS_ACCESS_FIFO + nv_iowr(NV_PGRAPH_FECS_ACCESS, 0, $r2) + + // setup i0 handler, and route all interrupts to it + mov $r1 #ih + mov $iv0 $r1 + + clear b32 $r2 + nv_iowr(NV_PGRAPH_FECS_INTR_ROUTE, 0, $r2) + + // route HUB_CHSW_PULSE to fuc interrupt 8 + mov $r2 0x2003 // { HUB_CHSW_PULSE, ZERO } -> intr 8 + nv_iowr(NV_PGRAPH_FECS_IROUTE, 0, $r2) + + // not sure what these are, route them because NVIDIA does, and + // the IRQ handler will signal the host if we ever get one.. we + // may find out if/why we need to handle these if so.. + // + mov $r2 0x2004 // { 0x04, ZERO } -> intr 9 + nv_iowr(NV_PGRAPH_FECS_IROUTE, 1, $r2) + mov $r2 0x200b // { HUB_FIRMWARE_MTHD, ZERO } -> intr 10 + nv_iowr(NV_PGRAPH_FECS_IROUTE, 2, $r2) + mov $r2 0x200c // { 0x0c, ZERO } -> intr 15 + nv_iowr(NV_PGRAPH_FECS_IROUTE, 7, $r2) + + // enable all INTR_UP interrupts + sub b32 $r3 $r0 1 + nv_iowr(NV_PGRAPH_FECS_INTR_UP_EN, 0, $r3) + + // enable fifo, ctxsw, 9, fwmthd, 15 interrupts + imm32($r2, 0x8704) + nv_iowr(NV_PGRAPH_FECS_INTR_EN_SET, 0, $r2) + + // fifo level triggered, rest edge + mov $r2 NV_PGRAPH_FECS_INTR_MODE_FIFO_LEVEL + nv_iowr(NV_PGRAPH_FECS_INTR_MODE, 0, $r2) + + // enable interrupts + bset $flags ie0 + + // fetch enabled GPC/ROP counts + nv_rd32($r14, 0x409604) + extr $r1 $r15 16:20 + st b32 D[$r0 + #rop_count] $r1 + and $r15 0x1f + st b32 D[$r0 + #gpc_count] $r15 + + // set BAR_REQMASK to GPC mask + mov $r1 1 + shl b32 $r1 $r15 + sub b32 $r1 1 + nv_iowr(NV_PGRAPH_FECS_BAR_MASK0, 0, $r1) + nv_iowr(NV_PGRAPH_FECS_BAR_MASK1, 0, $r1) + + // context size calculation, reserve first 256 bytes for use by fuc + mov $r1 256 + + // + mov $r15 2 + call(ctx_4170s) + call(ctx_4170w) + mov $r15 0x10 + call(ctx_86c) + + // calculate size of mmio context data + ld b32 $r14 D[$r0 + #hub_mmio_list_head] + ld b32 $r15 D[$r0 + #hub_mmio_list_tail] + call(mmctx_size) + + // set mmctx base addresses now so we don't have to do it later, + // they don't (currently) ever change + shr b32 $r4 $r1 8 + nv_iowr(NV_PGRAPH_FECS_MMCTX_SAVE_SWBASE, 0, $r4) + nv_iowr(NV_PGRAPH_FECS_MMCTX_LOAD_SWBASE, 0, $r4) + add b32 $r3 0x1300 + add b32 $r1 $r15 + shr b32 $r15 2 + nv_iowr(NV_PGRAPH_FECS_MMCTX_LOAD_COUNT, 0, $r15) // wtf?? + + // strands, base offset needs to be aligned to 256 bytes + shr b32 $r1 8 + add b32 $r1 1 + shl b32 $r1 8 + mov b32 $r15 $r1 + call(strand_ctx_init) + add b32 $r1 $r15 + + // initialise each GPC in sequence by passing in the offset of its + // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which + // has previously been uploaded by the host) running. + // + // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31 + // when it has completed, and return the size of its context data + // in GPCn_CC_SCRATCH[1] + // + ld b32 $r3 D[$r0 + #gpc_count] + imm32($r4, 0x502000) + init_gpc: + // setup, and start GPC ucode running + add b32 $r14 $r4 0x804 + mov b32 $r15 $r1 + call(nv_wr32) // CC_SCRATCH[1] = ctx offset + add b32 $r14 $r4 0x10c + clear b32 $r15 + call(nv_wr32) + add b32 $r14 $r4 0x104 + call(nv_wr32) // ENTRY + add b32 $r14 $r4 0x100 + mov $r15 2 // CTRL_START_TRIGGER + call(nv_wr32) // CTRL + + // wait for it to complete, and adjust context size + add b32 $r14 $r4 0x800 + init_gpc_wait: + call(nv_rd32) + xbit $r15 $r15 31 + bra e #init_gpc_wait + add b32 $r14 $r4 0x804 + call(nv_rd32) + add b32 $r1 $r15 + + // next! + add b32 $r4 0x8000 + sub b32 $r3 1 + bra ne #init_gpc + + // + mov $r15 0 + call(ctx_86c) + mov $r15 0 + call(ctx_4170s) + + // save context size, and tell host we're ready + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(1), 0, $r1) + clear b32 $r1 + bset $r1 31 + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(0), 0, $r1) + +// Main program loop, very simple, sleeps until woken up by the interrupt +// handler, pulls a command from the queue and executes its handler +// +wait: + // sleep until we have something to do + sleep $p0 + bset $flags $p0 +main: + mov $r13 #cmd_queue + call(queue_get) + bra $p1 #wait + + // context switch, requested by GPU? + cmpu b32 $r14 0x4001 + bra ne #main_not_ctx_switch + trace_set(T_AUTO) + nv_iord($r1, NV_PGRAPH_FECS_CHAN_ADDR, 0) + nv_iord($r2, NV_PGRAPH_FECS_CHAN_NEXT, 0) + + xbit $r3 $r1 31 + bra e #chsw_no_prev + xbit $r3 $r2 31 + bra e #chsw_prev_no_next + push $r2 + mov b32 $r2 $r1 + trace_set(T_SAVE) + bclr $flags $p1 + bset $flags $p2 + call(ctx_xfer) + trace_clr(T_SAVE); + pop $r2 + trace_set(T_LOAD); + bset $flags $p1 + call(ctx_xfer) + trace_clr(T_LOAD); + bra #chsw_done + chsw_prev_no_next: + push $r2 + mov b32 $r2 $r1 + bclr $flags $p1 + bclr $flags $p2 + call(ctx_xfer) + pop $r2 + nv_iowr(NV_PGRAPH_FECS_CHAN_ADDR, 0, $r2) + bra #chsw_done + chsw_no_prev: + xbit $r3 $r2 31 + bra e #chsw_done + bset $flags $p1 + bclr $flags $p2 + call(ctx_xfer) + + // ack the context switch request + chsw_done: + mov $r2 NV_PGRAPH_FECS_CHSW_ACK + nv_iowr(NV_PGRAPH_FECS_CHSW, 0, $r2) + trace_clr(T_AUTO) + bra #main + + // request to set current channel? (*not* a context switch) + main_not_ctx_switch: + cmpu b32 $r14 0x0001 + bra ne #main_not_ctx_chan + mov b32 $r2 $r15 + call(ctx_chan) + bra #main_done + + // request to store current channel context? + main_not_ctx_chan: + cmpu b32 $r14 0x0002 + bra ne #main_not_ctx_save + trace_set(T_SAVE) + bclr $flags $p1 + bclr $flags $p2 + call(ctx_xfer) + trace_clr(T_SAVE) + bra #main_done + + main_not_ctx_save: + shl b32 $r15 $r14 16 + or $r15 E_BAD_COMMAND + call(error) + bra #main + + main_done: + clear b32 $r2 + bset $r2 31 + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(0), 0, $r2) + bra #main + +// interrupt handler +ih: + push $r0 + push $r8 + mov $r8 $flags + push $r8 + push $r9 + push $r10 + push $r11 + push $r13 + push $r14 + push $r15 + clear b32 $r0 + + // incoming fifo command? + nv_iord($r10, NV_PGRAPH_FECS_INTR, 0) + and $r11 $r10 NV_PGRAPH_FECS_INTR_FIFO + bra e #ih_no_fifo + // queue incoming fifo command for later processing + mov $r13 #cmd_queue + nv_iord($r14, NV_PGRAPH_FECS_FIFO_CMD, 0) + nv_iord($r15, NV_PGRAPH_FECS_FIFO_DATA, 0) + call(queue_put) + add b32 $r11 0x400 + mov $r14 1 + nv_iowr(NV_PGRAPH_FECS_FIFO_ACK, 0, $r14) + + // context switch request? + ih_no_fifo: + and $r11 $r10 NV_PGRAPH_FECS_INTR_CHSW + bra e #ih_no_ctxsw + // enqueue a context switch for later processing + mov $r13 #cmd_queue + mov $r14 0x4001 + call(queue_put) + + // firmware method? + ih_no_ctxsw: + and $r11 $r10 NV_PGRAPH_FECS_INTR_FWMTHD + bra e #ih_no_fwmthd + // none we handle; report to host and ack + nv_rd32($r15, NV_PGRAPH_TRAPPED_DATA_LO) + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(4), 0, $r15) + nv_rd32($r15, NV_PGRAPH_TRAPPED_ADDR) + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(3), 0, $r15) + extr $r14 $r15 16:18 + shl b32 $r14 $r14 2 + imm32($r15, NV_PGRAPH_FE_OBJECT_TABLE(0)) + add b32 $r14 $r15 + call(nv_rd32) + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(2), 0, $r15) + mov $r15 E_BAD_FWMTHD + call(error) + mov $r11 0x100 + nv_wr32(0x400144, $r11) + + // anything we didn't handle, bring it to the host's attention + ih_no_fwmthd: + mov $r11 0x504 // FIFO | CHSW | FWMTHD + not b32 $r11 + and $r11 $r10 $r11 + bra e #ih_no_other + nv_iowr(NV_PGRAPH_FECS_INTR_UP_SET, 0, $r11) + + // ack, and wake up main() + ih_no_other: + nv_iowr(NV_PGRAPH_FECS_INTR_ACK, 0, $r10) + + pop $r15 + pop $r14 + pop $r13 + pop $r11 + pop $r10 + pop $r9 + pop $r8 + mov $flags $r8 + pop $r8 + pop $r0 + bclr $flags $p0 + iret + +#if CHIPSET < GK100 +// Not real sure, but, MEM_CMD 7 will hang forever if this isn't done +ctx_4160s: + mov $r15 1 + nv_wr32(0x404160, $r15) + ctx_4160s_wait: + nv_rd32($r15, 0x404160) + xbit $r15 $r15 4 + bra e #ctx_4160s_wait + ret + +// Without clearing again at end of xfer, some things cause PGRAPH +// to hang with STATUS=0x00000007 until it's cleared.. fbcon can +// still function with it set however... +ctx_4160c: + clear b32 $r15 + nv_wr32(0x404160, $r15) + ret +#endif + +// Again, not real sure +// +// In: $r15 value to set 0x404170 to +// +ctx_4170s: + or $r15 0x10 + nv_wr32(0x404170, $r15) + ret + +// Waits for a ctx_4170s() call to complete +// +ctx_4170w: + nv_rd32($r15, 0x404170) + and $r15 0x10 + bra ne #ctx_4170w + ret + +// Disables various things, waits a bit, and re-enables them.. +// +// Not sure how exactly this helps, perhaps "ENABLE" is not such a +// good description for the bits we turn off? Anyways, without this, +// funny things happen. +// +ctx_redswitch: + mov $r14 NV_PGRAPH_FECS_RED_SWITCH_ENABLE_GPC + or $r14 NV_PGRAPH_FECS_RED_SWITCH_POWER_ROP + or $r14 NV_PGRAPH_FECS_RED_SWITCH_POWER_GPC + or $r14 NV_PGRAPH_FECS_RED_SWITCH_POWER_MAIN + nv_iowr(NV_PGRAPH_FECS_RED_SWITCH, 0, $r14) + mov $r15 8 + ctx_redswitch_delay: + sub b32 $r15 1 + bra ne #ctx_redswitch_delay + or $r14 NV_PGRAPH_FECS_RED_SWITCH_ENABLE_ROP + or $r14 NV_PGRAPH_FECS_RED_SWITCH_ENABLE_MAIN + nv_iowr(NV_PGRAPH_FECS_RED_SWITCH, 0, $r14) + ret + +// Not a clue what this is for, except that unless the value is 0x10, the +// strand context is saved (and presumably restored) incorrectly.. +// +// In: $r15 value to set to (0x00/0x10 are used) +// +ctx_86c: + nv_iowr(NV_PGRAPH_FECS_UNK86C, 0, $r15) + nv_wr32(0x408a14, $r15) + nv_wr32(NV_PGRAPH_GPCX_GPCCS_UNK86C, $r15) + ret + +// In: $r15 NV_PGRAPH_FECS_MEM_CMD_* +ctx_mem: + nv_iowr(NV_PGRAPH_FECS_MEM_CMD, 0, $r15) + ctx_mem_wait: + nv_iord($r15, NV_PGRAPH_FECS_MEM_CMD, 0) + or $r15 $r15 + bra ne #ctx_mem_wait + ret + +// ctx_load - load's a channel's ctxctl data, and selects its vm +// +// In: $r2 channel address +// +ctx_load: + trace_set(T_CHAN) + + // switch to channel, somewhat magic in parts.. + mov $r10 12 // DONE_UNK12 + call(wait_donez) + clear b32 $r15 + nv_iowr(0x409a24, 0, $r15) + nv_iowr(NV_PGRAPH_FECS_CHAN_NEXT, 0, $r2) + nv_iowr(NV_PGRAPH_FECS_MEM_CHAN, 0, $r2) + mov $r15 NV_PGRAPH_FECS_MEM_CMD_LOAD_CHAN + call(ctx_mem) + nv_iowr(NV_PGRAPH_FECS_CHAN_ADDR, 0, $r2) + + // load channel header, fetch PGRAPH context pointer + mov $xtargets $r0 + bclr $r2 31 + shl b32 $r2 4 + add b32 $r2 2 + + trace_set(T_LCHAN) + nv_iowr(NV_PGRAPH_FECS_MEM_BASE, 0, $r2) + imm32($r2, NV_PGRAPH_FECS_MEM_TARGET_UNK31) + or $r2 NV_PGRAPH_FECS_MEM_TARGET_AS_VRAM + nv_iowr(NV_PGRAPH_FECS_MEM_TARGET, 0, $r2) + mov $r1 0x10 // chan + 0x0210 + mov $r2 #xfer_data + sethi $r2 0x00020000 // 16 bytes + xdld $r1 $r2 + xdwait + trace_clr(T_LCHAN) + + // update current context + ld b32 $r1 D[$r0 + #xfer_data + 4] + shl b32 $r1 24 + ld b32 $r2 D[$r0 + #xfer_data + 0] + shr b32 $r2 8 + or $r1 $r2 + st b32 D[$r0 + #ctx_current] $r1 + + // set transfer base to start of context, and fetch context header + trace_set(T_LCTXH) + nv_iowr(NV_PGRAPH_FECS_MEM_BASE, 0, $r1) + mov $r2 NV_PGRAPH_FECS_MEM_TARGET_AS_VM + nv_iowr(NV_PGRAPH_FECS_MEM_TARGET, 0, $r2) + mov $r1 #chan_data + sethi $r1 0x00060000 // 256 bytes + xdld $r0 $r1 + xdwait + trace_clr(T_LCTXH) + + trace_clr(T_CHAN) + ret + +// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as +// the active channel for ctxctl, but not actually transfer +// any context data. intended for use only during initial +// context construction. +// +// In: $r2 channel address +// +ctx_chan: +#if CHIPSET < GK100 + call(ctx_4160s) +#endif + call(ctx_load) + mov $r10 12 // DONE_UNK12 + call(wait_donez) + mov $r15 5 // MEM_CMD 5 ??? + call(ctx_mem) +#if CHIPSET < GK100 + call(ctx_4160c) +#endif + ret + +// Execute per-context state overrides list +// +// Only executed on the first load of a channel. Might want to look into +// removing this and having the host directly modify the channel's context +// to change this state... The nouveau DRM already builds this list as +// it's definitely needed for NVIDIA's, so we may as well use it for now +// +// Input: $r1 mmio list length +// +ctx_mmio_exec: + // set transfer base to be the mmio list + ld b32 $r3 D[$r0 + #chan_mmio_address] + nv_iowr(NV_PGRAPH_FECS_MEM_BASE, 0, $r3) + + clear b32 $r3 + ctx_mmio_loop: + // fetch next 256 bytes of mmio list if necessary + and $r4 $r3 0xff + bra ne #ctx_mmio_pull + mov $r5 #xfer_data + sethi $r5 0x00060000 // 256 bytes + xdld $r3 $r5 + xdwait + + // execute a single list entry + ctx_mmio_pull: + ld b32 $r14 D[$r4 + #xfer_data + 0x00] + ld b32 $r15 D[$r4 + #xfer_data + 0x04] + call(nv_wr32) + + // next! + add b32 $r3 8 + sub b32 $r1 1 + bra ne #ctx_mmio_loop + + // set transfer base back to the current context + ctx_mmio_done: + ld b32 $r3 D[$r0 + #ctx_current] + nv_iowr(NV_PGRAPH_FECS_MEM_BASE, 0, $r3) + + // disable the mmio list now, we don't need/want to execute it again + st b32 D[$r0 + #chan_mmio_count] $r0 + mov $r1 #chan_data + sethi $r1 0x00060000 // 256 bytes + xdst $r0 $r1 + xdwait + ret + +// Transfer HUB context data between GPU and storage area +// +// In: $r2 channel address +// $p1 clear on save, set on load +// $p2 set if opposite direction done/will be done, so: +// on save it means: "a load will follow this save" +// on load it means: "a save preceeded this load" +// +ctx_xfer: + // according to mwk, some kind of wait for idle + mov $r14 4 + nv_iowr(0x409c08, 0, $r14) + ctx_xfer_idle: + nv_iord($r14, 0x409c00, 0) + and $r14 0x2000 + bra ne #ctx_xfer_idle + + bra not $p1 #ctx_xfer_pre + bra $p2 #ctx_xfer_pre_load + ctx_xfer_pre: + mov $r15 0x10 + call(ctx_86c) +#if CHIPSET < GK100 + call(ctx_4160s) +#endif + bra not $p1 #ctx_xfer_exec + + ctx_xfer_pre_load: + mov $r15 2 + call(ctx_4170s) + call(ctx_4170w) + call(ctx_redswitch) + clear b32 $r15 + call(ctx_4170s) + call(ctx_load) + + // fetch context pointer, and initiate xfer on all GPCs + ctx_xfer_exec: + ld b32 $r1 D[$r0 + #ctx_current] + + clear b32 $r2 + nv_iowr(NV_PGRAPH_FECS_BAR, 0, $r2) + + nv_wr32(0x41a500, $r1) // GPC_BCAST_WRCMD_DATA = ctx pointer + xbit $r15 $flags $p1 + xbit $r2 $flags $p2 + shl b32 $r2 1 + or $r15 $r2 + nv_wr32(0x41a504, $r15) // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) + + // strands + call(strand_pre) + clear b32 $r2 + nv_iowr(NV_PGRAPH_FECS_STRAND_SELECT, 0x3f, $r2) + xbit $r2 $flags $p1 // SAVE/LOAD + add b32 $r2 NV_PGRAPH_FECS_STRAND_CMD_SAVE + nv_iowr(NV_PGRAPH_FECS_STRAND_CMD, 0x3f, $r2) + + // mmio context + xbit $r10 $flags $p1 // direction + or $r10 6 // first, last + mov $r11 0 // base = 0 + ld b32 $r12 D[$r0 + #hub_mmio_list_head] + ld b32 $r13 D[$r0 + #hub_mmio_list_tail] + mov $r14 0 // not multi + call(mmctx_xfer) + + // wait for GPCs to all complete + mov $r10 8 // DONE_BAR + call(wait_doneo) + + // wait for strand xfer to complete + call(strand_wait) + + // post-op + bra $p1 #ctx_xfer_post + mov $r10 12 // DONE_UNK12 + call(wait_donez) + mov $r15 5 // MEM_CMD 5 ??? + call(ctx_mem) + + bra $p2 #ctx_xfer_done + ctx_xfer_post: + mov $r15 2 + call(ctx_4170s) + clear b32 $r15 + call(ctx_86c) + call(strand_post) + call(ctx_4170w) + clear b32 $r15 + call(ctx_4170s) + + bra not $p1 #ctx_xfer_no_post_mmio + ld b32 $r1 D[$r0 + #chan_mmio_count] + or $r1 $r1 + bra e #ctx_xfer_no_post_mmio + call(ctx_mmio_exec) + + ctx_xfer_no_post_mmio: +#if CHIPSET < GK100 + call(ctx_4160c) +#endif + + ctx_xfer_done: + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 new file mode 100644 index 000000000..2c28e7199 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3 @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#define CHIPSET GF100 +#include "macros.fuc" + +.section #gf100_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #gf100_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h new file mode 100644 index 000000000..56162f6a6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf100.fuc3.h @@ -0,0 +1,1049 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gf100_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +static uint32_t gf100_grhub_code[] = { + 0x039b0ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x039b: init */ + 0xbd00f804, + 0x0007fe04, + 0x420017f1, + 0xcf0013f0, + 0x11e70011, + 0x14b60109, + 0x0014fe08, + 0xf10227f0, + 0xf0120007, + 0x02d00003, + 0xf104bd00, + 0xfe06c817, + 0x24bd0010, + 0x070007f1, + 0xd00003f0, + 0x04bd0002, + 0x200327f1, + 0x010007f1, + 0xd00103f0, + 0x04bd0002, + 0x200427f1, + 0x010407f1, + 0xd00103f0, + 0x04bd0002, + 0x200b27f1, + 0x010807f1, + 0xd00103f0, + 0x04bd0002, + 0x200c27f1, + 0x011c07f1, + 0xd00103f0, + 0x04bd0002, + 0xf1010392, + 0xf0090007, + 0x03d00303, + 0xf104bd00, + 0xf0870427, + 0x07f10023, + 0x03f00400, + 0x0002d000, + 0x27f004bd, + 0x0007f104, + 0x0003f003, + 0xbd0002d0, + 0x1031f404, + 0x9604e7f1, + 0xf440e3f0, + 0xfeb96821, + 0x90f1c702, + 0xf0030180, + 0x0f801ff4, + 0x0117f002, + 0xb6041fbb, + 0x07f10112, + 0x03f00300, + 0x0001d001, + 0x07f104bd, + 0x03f00400, + 0x0001d001, + 0x17f104bd, + 0xf7f00100, + 0x1121f502, + 0x2321f508, + 0x10f7f008, + 0x087021f5, + 0x98000e98, + 0x21f5010f, + 0x14950150, + 0x0007f108, + 0x0103f0c0, + 0xbd0004d0, + 0x0007f104, + 0x0103f0c1, + 0xbd0004d0, + 0x0030b704, + 0x001fbb13, + 0xf102f5b6, + 0xf0d30007, + 0x0fd00103, + 0xb604bd00, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb02d321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x04f4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea09d21, + 0xf4bd010c, + 0xa09d21f4, + 0xf401044e, + 0x4ea09d21, + 0xf7f00100, + 0x9d21f402, + 0x08004ea0, +/* 0x051c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, + 0x6821f408, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x00f7f0be, + 0x087021f5, + 0xf500f7f0, + 0xf1081121, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: wait */ + 0xf40028f4, +/* 0x056a: main */ + 0xd7f00031, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x4421f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f00f, + 0xbd0009d0, + 0x0131f404, + 0x0a4421f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a4421, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a4421f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff0e0ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0xd421f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x4421f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfebb0e, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea60ef5, +/* 0x06c8: ih */ + 0x80f900f9, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf0300bf4, + 0xe7f110d7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x00b0b704, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x071c: ih_no_fifo */ + 0x0100abe4, + 0xf00d0bf4, + 0xe7f110d7, + 0x21f44001, +/* 0x072d: ih_no_ctxsw */ + 0x00abe404, + 0x6c0bf404, + 0x0708e7f1, + 0xf440e3f0, + 0xffb96821, + 0x0007f102, + 0x0203f004, + 0xbd000fd0, + 0x04e7f104, + 0x40e3f007, + 0xb96821f4, + 0x07f102ff, + 0x03f00300, + 0x000fd002, + 0xfec704bd, + 0x02ee9450, + 0x0700f7f1, + 0xbb40f3f0, + 0x21f400ef, + 0x0007f168, + 0x0203f002, + 0xbd000fd0, + 0x03f7f004, + 0x037e21f5, + 0x0100b7f1, + 0xf102bfb9, + 0xf00144e7, + 0x21f440e3, +/* 0x079d: ih_no_fwmthd */ + 0x04b7f19d, + 0xffb0bd05, + 0x0bf4b4ab, + 0x0007f10f, + 0x0303f007, + 0xbd000bd0, +/* 0x07b5: ih_no_other */ + 0x0007f104, + 0x0003f001, + 0xbd000ad0, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0x32f400fc, +/* 0x07db: ctx_4160s */ + 0xf001f800, + 0xffb901f7, + 0x60e7f102, + 0x40e3f041, +/* 0x07eb: ctx_4160s_wait */ + 0xf19d21f4, + 0xf04160e7, + 0x21f440e3, + 0x02ffb968, + 0xf404ffc8, + 0x00f8f00b, +/* 0x0800: ctx_4160c */ + 0xffb9f4bd, + 0x60e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x0811: ctx_4170s */ + 0x10f5f000, + 0xf102ffb9, + 0xf04170e7, + 0x21f440e3, +/* 0x0823: ctx_4170w */ + 0xf100f89d, + 0xf04170e7, + 0x21f440e3, + 0x02ffb968, + 0xf410f4f0, + 0x00f8f01b, +/* 0x0838: ctx_redswitch */ + 0x0200e7f1, + 0xf040e5f0, + 0xe5f020e5, + 0x0007f110, + 0x0103f085, + 0xbd000ed0, + 0x08f7f004, +/* 0x0854: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x07f10100, + 0x03f08500, + 0x000ed001, + 0x00f804bd, +/* 0x0870: ctx_86c */ + 0x1b0007f1, + 0xd00203f0, + 0x04bd000f, + 0xf102ffb9, + 0xf08a14e7, + 0x21f440e3, + 0x02ffb99d, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f89d21, +/* 0x0898: ctx_mem */ + 0x840007f1, + 0xd00203f0, + 0x04bd000f, +/* 0x08a4: ctx_mem_wait */ + 0x8400f7f1, + 0xcf02f3f0, + 0xfffd00ff, + 0xf31bf405, +/* 0x08b6: ctx_load */ + 0x94bd00f8, + 0xf10599f0, + 0xf00f0007, + 0x09d00203, + 0xf004bd00, + 0x21f40ca7, + 0xf1f4bdd0, + 0xf0890007, + 0x0fd00203, + 0xf104bd00, + 0xf0c10007, + 0x02d00203, + 0xf104bd00, + 0xf0830007, + 0x02d00203, + 0xf004bd00, + 0x21f507f7, + 0x07f10898, + 0x03f0c000, + 0x0002d002, + 0x0bfe04bd, + 0x1f2af000, + 0xb60424b6, + 0x94bd0220, + 0xf10899f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, + 0xf0810007, + 0x02d00203, + 0xf104bd00, + 0xf1000027, + 0xf0800023, + 0x07f10225, + 0x03f08800, + 0x0002d002, + 0x17f004bd, + 0x0027f110, + 0x0223f002, + 0xf80512fa, + 0xf094bd03, + 0x07f10899, + 0x03f01700, + 0x0009d002, + 0x019804bd, + 0x1814b681, + 0xb6800298, + 0x12fd0825, + 0x16018005, + 0x99f094bd, + 0x0007f109, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f081, + 0xbd0001d0, + 0x0127f004, + 0x880007f1, + 0xd00203f0, + 0x04bd0002, + 0x010017f1, + 0xfa0613f0, + 0x03f80501, + 0x99f094bd, + 0x0007f109, + 0x0203f017, + 0xbd0009d0, + 0xf094bd04, + 0x07f10599, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x09d4: ctx_chan */ + 0x07db21f5, + 0x08b621f5, + 0xf40ca7f0, + 0xf7f0d021, + 0x9821f505, + 0x0021f508, +/* 0x09ef: ctx_mmio_exec */ + 0x9800f808, + 0x07f14103, + 0x03f08100, + 0x0003d002, + 0x34bd04bd, +/* 0x0a00: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x0a12: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0xb69d21f4, + 0x12b60830, + 0xdf1bf401, +/* 0x0a24: ctx_mmio_done */ + 0xf1160398, + 0xf0810007, + 0x03d00203, + 0x8004bd00, + 0x17f14000, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x0a44: ctx_xfer */ + 0xf104e7f0, + 0xf0020007, + 0x0ed00303, +/* 0x0a53: ctx_xfer_idle */ + 0xf104bd00, + 0xf00000e7, + 0xeecf03e3, + 0x00e4f100, + 0xf21bf420, + 0xf40611f4, +/* 0x0a6a: ctx_xfer_pre */ + 0xf7f01102, + 0x7021f510, + 0xdb21f508, + 0x1c11f407, +/* 0x0a78: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5081121, + 0xf5082321, + 0xbd083821, + 0x1121f5f4, + 0xb621f508, +/* 0x0a91: ctx_xfer_exec */ + 0x16019808, + 0x07f124bd, + 0x03f00500, + 0x0002d001, + 0x1fb904bd, + 0x00e7f102, + 0x41e3f0a5, + 0xf09d21f4, + 0x2cf001fc, + 0x0124b602, + 0xb905f2fd, + 0xe7f102ff, + 0xe3f0a504, + 0x9d21f441, + 0x026a21f5, + 0x07f124bd, + 0x03f047fc, + 0x0002d002, + 0x2cf004bd, + 0x0320b601, + 0x4afc07f1, + 0xd00203f0, + 0x04bd0002, + 0xf001acf0, + 0xb7f006a5, + 0x000c9800, + 0xf0010d98, + 0x21f500e7, + 0xa7f0016f, + 0x1021f508, + 0x5e21f501, + 0x1301f402, + 0xf40ca7f0, + 0xf7f0d021, + 0x9821f505, + 0x3202f408, +/* 0x0b20: ctx_xfer_post */ + 0xf502f7f0, + 0xbd081121, + 0x7021f5f4, + 0x7f21f508, + 0x2321f502, + 0xf5f4bd08, + 0xf4081121, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x0b4b: ctx_xfer_no_post_mmio */ + 0xf509ef21, +/* 0x0b4f: ctx_xfer_done */ + 0xf8080021, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 new file mode 100644 index 000000000..581b2d53a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3 @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#define CHIPSET GF117 +#include "macros.fuc" + +.section #gf117_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #gf117_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h new file mode 100644 index 000000000..9b9f0d93f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgf117.fuc3.h @@ -0,0 +1,1049 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gf117_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +static uint32_t gf117_grhub_code[] = { + 0x039b0ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x039b: init */ + 0xbd00f804, + 0x0007fe04, + 0x420017f1, + 0xcf0013f0, + 0x11e70011, + 0x14b60109, + 0x0014fe08, + 0xf10227f0, + 0xf0120007, + 0x02d00003, + 0xf104bd00, + 0xfe06c817, + 0x24bd0010, + 0x070007f1, + 0xd00003f0, + 0x04bd0002, + 0x200327f1, + 0x010007f1, + 0xd00103f0, + 0x04bd0002, + 0x200427f1, + 0x010407f1, + 0xd00103f0, + 0x04bd0002, + 0x200b27f1, + 0x010807f1, + 0xd00103f0, + 0x04bd0002, + 0x200c27f1, + 0x011c07f1, + 0xd00103f0, + 0x04bd0002, + 0xf1010392, + 0xf0090007, + 0x03d00303, + 0xf104bd00, + 0xf0870427, + 0x07f10023, + 0x03f00400, + 0x0002d000, + 0x27f004bd, + 0x0007f104, + 0x0003f003, + 0xbd0002d0, + 0x1031f404, + 0x9604e7f1, + 0xf440e3f0, + 0xfeb96821, + 0x90f1c702, + 0xf0030180, + 0x0f801ff4, + 0x0117f002, + 0xb6041fbb, + 0x07f10112, + 0x03f00300, + 0x0001d001, + 0x07f104bd, + 0x03f00400, + 0x0001d001, + 0x17f104bd, + 0xf7f00100, + 0x1121f502, + 0x2321f508, + 0x10f7f008, + 0x087021f5, + 0x98000e98, + 0x21f5010f, + 0x14950150, + 0x0007f108, + 0x0103f0c0, + 0xbd0004d0, + 0x0007f104, + 0x0103f0c1, + 0xbd0004d0, + 0x0030b704, + 0x001fbb13, + 0xf102f5b6, + 0xf0d30007, + 0x0fd00103, + 0xb604bd00, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb02d321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x04f4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea09d21, + 0xf4bd010c, + 0xa09d21f4, + 0xf401044e, + 0x4ea09d21, + 0xf7f00100, + 0x9d21f402, + 0x08004ea0, +/* 0x051c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, + 0x6821f408, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x00f7f0be, + 0x087021f5, + 0xf500f7f0, + 0xf1081121, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: wait */ + 0xf40028f4, +/* 0x056a: main */ + 0xd7f00031, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x4421f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f00f, + 0xbd0009d0, + 0x0131f404, + 0x0a4421f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a4421, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a4421f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff0e0ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0xd421f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x4421f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfebb0e, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea60ef5, +/* 0x06c8: ih */ + 0x80f900f9, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf0300bf4, + 0xe7f110d7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x00b0b704, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x071c: ih_no_fifo */ + 0x0100abe4, + 0xf00d0bf4, + 0xe7f110d7, + 0x21f44001, +/* 0x072d: ih_no_ctxsw */ + 0x00abe404, + 0x6c0bf404, + 0x0708e7f1, + 0xf440e3f0, + 0xffb96821, + 0x0007f102, + 0x0203f004, + 0xbd000fd0, + 0x04e7f104, + 0x40e3f007, + 0xb96821f4, + 0x07f102ff, + 0x03f00300, + 0x000fd002, + 0xfec704bd, + 0x02ee9450, + 0x0700f7f1, + 0xbb40f3f0, + 0x21f400ef, + 0x0007f168, + 0x0203f002, + 0xbd000fd0, + 0x03f7f004, + 0x037e21f5, + 0x0100b7f1, + 0xf102bfb9, + 0xf00144e7, + 0x21f440e3, +/* 0x079d: ih_no_fwmthd */ + 0x04b7f19d, + 0xffb0bd05, + 0x0bf4b4ab, + 0x0007f10f, + 0x0303f007, + 0xbd000bd0, +/* 0x07b5: ih_no_other */ + 0x0007f104, + 0x0003f001, + 0xbd000ad0, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0x32f400fc, +/* 0x07db: ctx_4160s */ + 0xf001f800, + 0xffb901f7, + 0x60e7f102, + 0x40e3f041, +/* 0x07eb: ctx_4160s_wait */ + 0xf19d21f4, + 0xf04160e7, + 0x21f440e3, + 0x02ffb968, + 0xf404ffc8, + 0x00f8f00b, +/* 0x0800: ctx_4160c */ + 0xffb9f4bd, + 0x60e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x0811: ctx_4170s */ + 0x10f5f000, + 0xf102ffb9, + 0xf04170e7, + 0x21f440e3, +/* 0x0823: ctx_4170w */ + 0xf100f89d, + 0xf04170e7, + 0x21f440e3, + 0x02ffb968, + 0xf410f4f0, + 0x00f8f01b, +/* 0x0838: ctx_redswitch */ + 0x0200e7f1, + 0xf040e5f0, + 0xe5f020e5, + 0x0007f110, + 0x0103f085, + 0xbd000ed0, + 0x08f7f004, +/* 0x0854: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x07f10100, + 0x03f08500, + 0x000ed001, + 0x00f804bd, +/* 0x0870: ctx_86c */ + 0x1b0007f1, + 0xd00203f0, + 0x04bd000f, + 0xf102ffb9, + 0xf08a14e7, + 0x21f440e3, + 0x02ffb99d, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f89d21, +/* 0x0898: ctx_mem */ + 0x840007f1, + 0xd00203f0, + 0x04bd000f, +/* 0x08a4: ctx_mem_wait */ + 0x8400f7f1, + 0xcf02f3f0, + 0xfffd00ff, + 0xf31bf405, +/* 0x08b6: ctx_load */ + 0x94bd00f8, + 0xf10599f0, + 0xf00f0007, + 0x09d00203, + 0xf004bd00, + 0x21f40ca7, + 0xf1f4bdd0, + 0xf0890007, + 0x0fd00203, + 0xf104bd00, + 0xf0c10007, + 0x02d00203, + 0xf104bd00, + 0xf0830007, + 0x02d00203, + 0xf004bd00, + 0x21f507f7, + 0x07f10898, + 0x03f0c000, + 0x0002d002, + 0x0bfe04bd, + 0x1f2af000, + 0xb60424b6, + 0x94bd0220, + 0xf10899f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, + 0xf0810007, + 0x02d00203, + 0xf104bd00, + 0xf1000027, + 0xf0800023, + 0x07f10225, + 0x03f08800, + 0x0002d002, + 0x17f004bd, + 0x0027f110, + 0x0223f002, + 0xf80512fa, + 0xf094bd03, + 0x07f10899, + 0x03f01700, + 0x0009d002, + 0x019804bd, + 0x1814b681, + 0xb6800298, + 0x12fd0825, + 0x16018005, + 0x99f094bd, + 0x0007f109, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f081, + 0xbd0001d0, + 0x0127f004, + 0x880007f1, + 0xd00203f0, + 0x04bd0002, + 0x010017f1, + 0xfa0613f0, + 0x03f80501, + 0x99f094bd, + 0x0007f109, + 0x0203f017, + 0xbd0009d0, + 0xf094bd04, + 0x07f10599, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x09d4: ctx_chan */ + 0x07db21f5, + 0x08b621f5, + 0xf40ca7f0, + 0xf7f0d021, + 0x9821f505, + 0x0021f508, +/* 0x09ef: ctx_mmio_exec */ + 0x9800f808, + 0x07f14103, + 0x03f08100, + 0x0003d002, + 0x34bd04bd, +/* 0x0a00: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x0a12: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0xb69d21f4, + 0x12b60830, + 0xdf1bf401, +/* 0x0a24: ctx_mmio_done */ + 0xf1160398, + 0xf0810007, + 0x03d00203, + 0x8004bd00, + 0x17f14000, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x0a44: ctx_xfer */ + 0xf104e7f0, + 0xf0020007, + 0x0ed00303, +/* 0x0a53: ctx_xfer_idle */ + 0xf104bd00, + 0xf00000e7, + 0xeecf03e3, + 0x00e4f100, + 0xf21bf420, + 0xf40611f4, +/* 0x0a6a: ctx_xfer_pre */ + 0xf7f01102, + 0x7021f510, + 0xdb21f508, + 0x1c11f407, +/* 0x0a78: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5081121, + 0xf5082321, + 0xbd083821, + 0x1121f5f4, + 0xb621f508, +/* 0x0a91: ctx_xfer_exec */ + 0x16019808, + 0x07f124bd, + 0x03f00500, + 0x0002d001, + 0x1fb904bd, + 0x00e7f102, + 0x41e3f0a5, + 0xf09d21f4, + 0x2cf001fc, + 0x0124b602, + 0xb905f2fd, + 0xe7f102ff, + 0xe3f0a504, + 0x9d21f441, + 0x026a21f5, + 0x07f124bd, + 0x03f047fc, + 0x0002d002, + 0x2cf004bd, + 0x0320b601, + 0x4afc07f1, + 0xd00203f0, + 0x04bd0002, + 0xf001acf0, + 0xb7f006a5, + 0x000c9800, + 0xf0010d98, + 0x21f500e7, + 0xa7f0016f, + 0x1021f508, + 0x5e21f501, + 0x1301f402, + 0xf40ca7f0, + 0xf7f0d021, + 0x9821f505, + 0x3202f408, +/* 0x0b20: ctx_xfer_post */ + 0xf502f7f0, + 0xbd081121, + 0x7021f5f4, + 0x7f21f508, + 0x2321f502, + 0xf5f4bd08, + 0xf4081121, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x0b4b: ctx_xfer_no_post_mmio */ + 0xf509ef21, +/* 0x0b4f: ctx_xfer_done */ + 0xf8080021, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 new file mode 100644 index 000000000..d977d393b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3 @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#define CHIPSET GK100 +#include "macros.fuc" + +.section #gk104_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #gk104_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h new file mode 100644 index 000000000..fa11857b9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk104.fuc3.h @@ -0,0 +1,1046 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gk104_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +static uint32_t gk104_grhub_code[] = { + 0x039b0ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x039b: init */ + 0xbd00f804, + 0x0007fe04, + 0x420017f1, + 0xcf0013f0, + 0x11e70011, + 0x14b60109, + 0x0014fe08, + 0xf10227f0, + 0xf0120007, + 0x02d00003, + 0xf104bd00, + 0xfe06c817, + 0x24bd0010, + 0x070007f1, + 0xd00003f0, + 0x04bd0002, + 0x200327f1, + 0x010007f1, + 0xd00103f0, + 0x04bd0002, + 0x200427f1, + 0x010407f1, + 0xd00103f0, + 0x04bd0002, + 0x200b27f1, + 0x010807f1, + 0xd00103f0, + 0x04bd0002, + 0x200c27f1, + 0x011c07f1, + 0xd00103f0, + 0x04bd0002, + 0xf1010392, + 0xf0090007, + 0x03d00303, + 0xf104bd00, + 0xf0870427, + 0x07f10023, + 0x03f00400, + 0x0002d000, + 0x27f004bd, + 0x0007f104, + 0x0003f003, + 0xbd0002d0, + 0x1031f404, + 0x9604e7f1, + 0xf440e3f0, + 0xfeb96821, + 0x90f1c702, + 0xf0030180, + 0x0f801ff4, + 0x0117f002, + 0xb6041fbb, + 0x07f10112, + 0x03f00300, + 0x0001d001, + 0x07f104bd, + 0x03f00400, + 0x0001d001, + 0x17f104bd, + 0xf7f00100, + 0xdb21f502, + 0xed21f507, + 0x10f7f007, + 0x083a21f5, + 0x98000e98, + 0x21f5010f, + 0x14950150, + 0x0007f108, + 0x0103f0c0, + 0xbd0004d0, + 0x0007f104, + 0x0103f0c1, + 0xbd0004d0, + 0x0030b704, + 0x001fbb13, + 0xf102f5b6, + 0xf0d30007, + 0x0fd00103, + 0xb604bd00, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb02d321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x04f4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea09d21, + 0xf4bd010c, + 0xa09d21f4, + 0xf401044e, + 0x4ea09d21, + 0xf7f00100, + 0x9d21f402, + 0x08004ea0, +/* 0x051c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, + 0x6821f408, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x00f7f0be, + 0x083a21f5, + 0xf500f7f0, + 0xf107db21, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: wait */ + 0xf40028f4, +/* 0x056a: main */ + 0xd7f00031, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x0621f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f00f, + 0xbd0009d0, + 0x0131f404, + 0x0a0621f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a0621, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a0621f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff0e0ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0x9e21f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf00f0007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x0621f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfebb0e, + 0x1f29f024, + 0x080007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea60ef5, +/* 0x06c8: ih */ + 0x80f900f9, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf0300bf4, + 0xe7f110d7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x00b0b704, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x071c: ih_no_fifo */ + 0x0100abe4, + 0xf00d0bf4, + 0xe7f110d7, + 0x21f44001, +/* 0x072d: ih_no_ctxsw */ + 0x00abe404, + 0x6c0bf404, + 0x0708e7f1, + 0xf440e3f0, + 0xffb96821, + 0x0007f102, + 0x0203f004, + 0xbd000fd0, + 0x04e7f104, + 0x40e3f007, + 0xb96821f4, + 0x07f102ff, + 0x03f00300, + 0x000fd002, + 0xfec704bd, + 0x02ee9450, + 0x0700f7f1, + 0xbb40f3f0, + 0x21f400ef, + 0x0007f168, + 0x0203f002, + 0xbd000fd0, + 0x03f7f004, + 0x037e21f5, + 0x0100b7f1, + 0xf102bfb9, + 0xf00144e7, + 0x21f440e3, +/* 0x079d: ih_no_fwmthd */ + 0x04b7f19d, + 0xffb0bd05, + 0x0bf4b4ab, + 0x0007f10f, + 0x0303f007, + 0xbd000bd0, +/* 0x07b5: ih_no_other */ + 0x0007f104, + 0x0003f001, + 0xbd000ad0, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0x32f400fc, +/* 0x07db: ctx_4170s */ + 0xf001f800, + 0xffb910f5, + 0x70e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x07ed: ctx_4170w */ + 0x70e7f100, + 0x40e3f041, + 0xb96821f4, + 0xf4f002ff, + 0xf01bf410, +/* 0x0802: ctx_redswitch */ + 0xe7f100f8, + 0xe5f00200, + 0x20e5f040, + 0xf110e5f0, + 0xf0850007, + 0x0ed00103, + 0xf004bd00, +/* 0x081e: ctx_redswitch_delay */ + 0xf2b608f7, + 0xfd1bf401, + 0x0400e5f1, + 0x0100e5f1, + 0x850007f1, + 0xd00103f0, + 0x04bd000e, +/* 0x083a: ctx_86c */ + 0x07f100f8, + 0x03f01b00, + 0x000fd002, + 0xffb904bd, + 0x14e7f102, + 0x40e3f08a, + 0xb99d21f4, + 0xe7f102ff, + 0xe3f0a86c, + 0x9d21f441, +/* 0x0862: ctx_mem */ + 0x07f100f8, + 0x03f08400, + 0x000fd002, +/* 0x086e: ctx_mem_wait */ + 0xf7f104bd, + 0xf3f08400, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f31b, +/* 0x0880: ctx_load */ + 0x99f094bd, + 0x0007f105, + 0x0203f00f, + 0xbd0009d0, + 0x0ca7f004, + 0xbdd021f4, + 0x0007f1f4, + 0x0203f089, + 0xbd000fd0, + 0x0007f104, + 0x0203f0c1, + 0xbd0002d0, + 0x0007f104, + 0x0203f083, + 0xbd0002d0, + 0x07f7f004, + 0x086221f5, + 0xc00007f1, + 0xd00203f0, + 0x04bd0002, + 0xf0000bfe, + 0x24b61f2a, + 0x0220b604, + 0x99f094bd, + 0x0007f108, + 0x0203f00f, + 0xbd0009d0, + 0x0007f104, + 0x0203f081, + 0xbd0002d0, + 0x0027f104, + 0x0023f100, + 0x0225f080, + 0x880007f1, + 0xd00203f0, + 0x04bd0002, + 0xf11017f0, + 0xf0020027, + 0x12fa0223, + 0xbd03f805, + 0x0899f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xb6810198, + 0x02981814, + 0x0825b680, + 0x800512fd, + 0x94bd1601, + 0xf10999f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, + 0xf0810007, + 0x01d00203, + 0xf004bd00, + 0x07f10127, + 0x03f08800, + 0x0002d002, + 0x17f104bd, + 0x13f00100, + 0x0501fa06, + 0x94bd03f8, + 0xf10999f0, + 0xf0170007, + 0x09d00203, + 0xbd04bd00, + 0x0599f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x099e: ctx_chan */ + 0x21f500f8, + 0xa7f00880, + 0xd021f40c, + 0xf505f7f0, + 0xf8086221, +/* 0x09b1: ctx_mmio_exec */ + 0x41039800, + 0x810007f1, + 0xd00203f0, + 0x04bd0003, +/* 0x09c2: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x09d4: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b69d, + 0xf40112b6, +/* 0x09e6: ctx_mmio_done */ + 0x0398df1b, + 0x0007f116, + 0x0203f081, + 0xbd0003d0, + 0x40008004, + 0x010017f1, + 0xfa0613f0, + 0x03f80601, +/* 0x0a06: ctx_xfer */ + 0xe7f000f8, + 0x0007f104, + 0x0303f002, + 0xbd000ed0, +/* 0x0a15: ctx_xfer_idle */ + 0x00e7f104, + 0x03e3f000, + 0xf100eecf, + 0xf42000e4, + 0x11f4f21b, + 0x0d02f406, +/* 0x0a2c: ctx_xfer_pre */ + 0xf510f7f0, + 0xf4083a21, +/* 0x0a36: ctx_xfer_pre_load */ + 0xf7f01c11, + 0xdb21f502, + 0xed21f507, + 0x0221f507, + 0xf5f4bd08, + 0xf507db21, +/* 0x0a4f: ctx_xfer_exec */ + 0x98088021, + 0x24bd1601, + 0x050007f1, + 0xd00103f0, + 0x04bd0002, + 0xf1021fb9, + 0xf0a500e7, + 0x21f441e3, + 0x01fcf09d, + 0xb6022cf0, + 0xf2fd0124, + 0x02ffb905, + 0xa504e7f1, + 0xf441e3f0, + 0x21f59d21, + 0x24bd026a, + 0x47fc07f1, + 0xd00203f0, + 0x04bd0002, + 0xb6012cf0, + 0x07f10320, + 0x03f04afc, + 0x0002d002, + 0xacf004bd, + 0x06a5f001, + 0x9800b7f0, + 0x0d98000c, + 0x00e7f001, + 0x016f21f5, + 0xf508a7f0, + 0xf5011021, + 0xf4025e21, + 0xa7f01301, + 0xd021f40c, + 0xf505f7f0, + 0xf4086221, +/* 0x0ade: ctx_xfer_post */ + 0xf7f02e02, + 0xdb21f502, + 0xf5f4bd07, + 0xf5083a21, + 0xf5027f21, + 0xbd07ed21, + 0xdb21f5f4, + 0x1011f407, + 0xfd400198, + 0x0bf40511, + 0xb121f507, +/* 0x0b09: ctx_xfer_no_post_mmio */ +/* 0x0b09: ctx_xfer_done */ + 0x0000f809, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 new file mode 100644 index 000000000..760b4632f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3 @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#define CHIPSET GK110 +#include "macros.fuc" + +.section #gk110_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #gk110_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h new file mode 100644 index 000000000..1d741b30a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk110.fuc3.h @@ -0,0 +1,1046 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gk110_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +static uint32_t gk110_grhub_code[] = { + 0x039b0ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f8037e, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0xf002ecb9, + 0x07f11fc9, + 0x03f0ca00, + 0x000cd001, +/* 0x007a: nv_rd32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0xa7f0f31b, + 0x1021f506, + 0x00f7f101, + 0x01f3f0cb, + 0xf800ffcf, +/* 0x009d: nv_wr32 */ + 0x0007f100, + 0x0103f0cc, + 0xbd000fd0, + 0x02ecb904, + 0xf01fc9f0, + 0x07f11ec9, + 0x03f0ca00, + 0x000cd001, +/* 0x00be: nv_wr32_wait */ + 0xc7f104bd, + 0xc3f0ca00, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f31b, +/* 0x00d0: wait_donez */ + 0x99f094bd, + 0x0007f100, + 0x0203f037, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x00ed: wait_donez_ne */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x1bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0110: wait_doneo */ + 0x99f094bd, + 0x0007f100, + 0x0203f037, + 0xbd0009d0, + 0x0007f104, + 0x0203f006, + 0xbd000ad0, +/* 0x012d: wait_doneo_e */ + 0x0087f104, + 0x0183f000, + 0xff0088cf, + 0x0bf4888a, + 0xf094bdf3, + 0x07f10099, + 0x03f01700, + 0x0009d002, + 0x00f804bd, +/* 0x0150: mmctx_size */ +/* 0x0152: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0xf404efb8, + 0x9fb9eb1b, +/* 0x016f: mmctx_xfer */ + 0xbd00f802, + 0x0199f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0xbbfd94bd, + 0x120bf405, + 0xc40007f1, + 0xd00103f0, + 0x04bd000b, +/* 0x0197: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0x0007f11e, + 0x0103f0c6, + 0xbd000ed0, + 0x0007f104, + 0x0103f0c7, + 0xbd000fd0, + 0x0199f004, +/* 0x01b8: mmctx_multi_disabled */ + 0xb600abc8, + 0xb9f010b4, + 0x01aec80c, + 0xfd11e4b6, + 0x07f105be, + 0x03f0c500, + 0x000bd001, +/* 0x01d6: mmctx_exec_loop */ +/* 0x01d6: mmctx_wait_free */ + 0xe7f104bd, + 0xe3f0c500, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f30b, + 0x05e9fd00, + 0xc80007f1, + 0xd00103f0, + 0x04bd000e, + 0xb804c0b6, + 0x1bf404cd, + 0x02abc8d8, +/* 0x0207: mmctx_fini_wait */ + 0xf11f1bf4, + 0xf0c500b7, + 0xbbcf01b3, + 0x1fb4f000, + 0xf410b4b0, + 0xa7f0f01b, + 0xd021f405, +/* 0x0223: mmctx_stop */ + 0xc82b0ef4, + 0xb4b600ab, + 0x0cb9f010, + 0xf112b9f0, + 0xf0c50007, + 0x0bd00103, +/* 0x023b: mmctx_stop_wait */ + 0xf104bd00, + 0xf0c500b7, + 0xbbcf01b3, + 0x12bbc800, +/* 0x024b: mmctx_done */ + 0xbdf31bf4, + 0x0199f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x025e: strand_wait */ + 0xa0f900f8, + 0xf402a7f0, + 0xa0fcd021, +/* 0x026a: strand_pre */ + 0x97f000f8, + 0xfc07f10c, + 0x0203f04a, + 0xbd0009d0, + 0x5e21f504, +/* 0x027f: strand_post */ + 0xf000f802, + 0x07f10d97, + 0x03f04afc, + 0x0009d002, + 0x21f504bd, + 0x00f8025e, +/* 0x0294: strand_set */ + 0xf10fc7f0, + 0xf04ffc07, + 0x0cd00203, + 0xf004bd00, + 0x07f10bc7, + 0x03f04afc, + 0x000cd002, + 0x07f104bd, + 0x03f04ffc, + 0x000ed002, + 0xc7f004bd, + 0xfc07f10a, + 0x0203f04a, + 0xbd000cd0, + 0x5e21f504, +/* 0x02d3: strand_ctx_init */ + 0xbd00f802, + 0x0399f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0x026a21f5, + 0xf503e7f0, + 0xbd029421, + 0xfc07f1c4, + 0x0203f047, + 0xbd000cd0, + 0x01c7f004, + 0x4afc07f1, + 0xd00203f0, + 0x04bd000c, + 0x025e21f5, + 0xf1010c92, + 0xf046fc07, + 0x0cd00203, + 0xf004bd00, + 0x07f102c7, + 0x03f04afc, + 0x000cd002, + 0x21f504bd, + 0x21f5025e, + 0x87f1027f, + 0x83f04200, + 0x0097f102, + 0x0293f020, + 0x950099cf, +/* 0x034a: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x037e: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x039b: init */ + 0xbd00f804, + 0x0007fe04, + 0x420017f1, + 0xcf0013f0, + 0x11e70011, + 0x14b60109, + 0x0014fe08, + 0xf10227f0, + 0xf0120007, + 0x02d00003, + 0xf104bd00, + 0xfe06c817, + 0x24bd0010, + 0x070007f1, + 0xd00003f0, + 0x04bd0002, + 0x200327f1, + 0x010007f1, + 0xd00103f0, + 0x04bd0002, + 0x200427f1, + 0x010407f1, + 0xd00103f0, + 0x04bd0002, + 0x200b27f1, + 0x010807f1, + 0xd00103f0, + 0x04bd0002, + 0x200c27f1, + 0x011c07f1, + 0xd00103f0, + 0x04bd0002, + 0xf1010392, + 0xf0090007, + 0x03d00303, + 0xf104bd00, + 0xf0870427, + 0x07f10023, + 0x03f00400, + 0x0002d000, + 0x27f004bd, + 0x0007f104, + 0x0003f003, + 0xbd0002d0, + 0x1031f404, + 0x9604e7f1, + 0xf440e3f0, + 0xfeb96821, + 0x90f1c702, + 0xf0030180, + 0x0f801ff4, + 0x0117f002, + 0xb6041fbb, + 0x07f10112, + 0x03f00300, + 0x0001d001, + 0x07f104bd, + 0x03f00400, + 0x0001d001, + 0x17f104bd, + 0xf7f00100, + 0xdb21f502, + 0xed21f507, + 0x10f7f007, + 0x083a21f5, + 0x98000e98, + 0x21f5010f, + 0x14950150, + 0x0007f108, + 0x0103f0c0, + 0xbd0004d0, + 0x0007f104, + 0x0103f0c1, + 0xbd0004d0, + 0x0030b704, + 0x001fbb13, + 0xf102f5b6, + 0xf0d30007, + 0x0fd00103, + 0xb604bd00, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb02d321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x04f4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea09d21, + 0xf4bd010c, + 0xa09d21f4, + 0xf401044e, + 0x4ea09d21, + 0xf7f00100, + 0x9d21f402, + 0x08004ea0, +/* 0x051c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, + 0x6821f408, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x00f7f0be, + 0x083a21f5, + 0xf500f7f0, + 0xf107db21, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, + 0x1f19f014, + 0x300007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0564: wait */ + 0xf40028f4, +/* 0x056a: main */ + 0xd7f00031, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00e91b, + 0x0499f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0xc00017f1, + 0xcf0213f0, + 0x27f10011, + 0x23f0c100, + 0x0022cf02, + 0xf51f13c8, + 0xc800890b, + 0x0bf41f23, + 0xb920f962, + 0x94bd0212, + 0xf10799f0, + 0xf0370007, + 0x09d00203, + 0xf404bd00, + 0x31f40132, + 0x0621f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x20fc04bd, + 0x99f094bd, + 0x0007f106, + 0x0203f037, + 0xbd0009d0, + 0x0131f404, + 0x0a0621f5, + 0x99f094bd, + 0x0007f106, + 0x0203f017, + 0xbd0009d0, + 0x330ef404, +/* 0x060c: chsw_prev_no_next */ + 0x12b920f9, + 0x0132f402, + 0xf50232f4, + 0xfc0a0621, + 0x0007f120, + 0x0203f0c0, + 0xbd0002d0, + 0x130ef404, +/* 0x062c: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x0a0621f5, +/* 0x063c: chsw_done */ + 0xf10127f0, + 0xf0c30007, + 0x02d00203, + 0xbd04bd00, + 0x0499f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xff0e0ef5, +/* 0x0660: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0x9e21f502, + 0x460ef409, +/* 0x0670: main_not_ctx_chan */ + 0xf402e4b0, + 0x94bd321b, + 0xf10799f0, + 0xf0370007, + 0x09d00203, + 0xf404bd00, + 0x32f40132, + 0x0621f502, + 0xf094bd0a, + 0x07f10799, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x06a5: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf5037e21, +/* 0x06b3: main_done */ + 0xbdfebb0e, + 0x1f29f024, + 0x300007f1, + 0xd00203f0, + 0x04bd0002, + 0xfea60ef5, +/* 0x06c8: ih */ + 0x80f900f9, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xf104bdf0, + 0xf00200a7, + 0xaacf00a3, + 0x04abc400, + 0xf0300bf4, + 0xe7f110d7, + 0xe3f01a00, + 0x00eecf00, + 0x1900f7f1, + 0xcf00f3f0, + 0x21f400ff, + 0x00b0b704, + 0x01e7f004, + 0x1d0007f1, + 0xd00003f0, + 0x04bd000e, +/* 0x071c: ih_no_fifo */ + 0x0100abe4, + 0xf00d0bf4, + 0xe7f110d7, + 0x21f44001, +/* 0x072d: ih_no_ctxsw */ + 0x00abe404, + 0x6c0bf404, + 0x0708e7f1, + 0xf440e3f0, + 0xffb96821, + 0x0007f102, + 0x0203f004, + 0xbd000fd0, + 0x04e7f104, + 0x40e3f007, + 0xb96821f4, + 0x07f102ff, + 0x03f00300, + 0x000fd002, + 0xfec704bd, + 0x02ee9450, + 0x0700f7f1, + 0xbb40f3f0, + 0x21f400ef, + 0x0007f168, + 0x0203f002, + 0xbd000fd0, + 0x03f7f004, + 0x037e21f5, + 0x0100b7f1, + 0xf102bfb9, + 0xf00144e7, + 0x21f440e3, +/* 0x079d: ih_no_fwmthd */ + 0x04b7f19d, + 0xffb0bd05, + 0x0bf4b4ab, + 0x0007f10f, + 0x0303f007, + 0xbd000bd0, +/* 0x07b5: ih_no_other */ + 0x0007f104, + 0x0003f001, + 0xbd000ad0, + 0xfcf0fc04, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0x32f400fc, +/* 0x07db: ctx_4170s */ + 0xf001f800, + 0xffb910f5, + 0x70e7f102, + 0x40e3f041, + 0xf89d21f4, +/* 0x07ed: ctx_4170w */ + 0x70e7f100, + 0x40e3f041, + 0xb96821f4, + 0xf4f002ff, + 0xf01bf410, +/* 0x0802: ctx_redswitch */ + 0xe7f100f8, + 0xe5f00200, + 0x20e5f040, + 0xf110e5f0, + 0xf0850007, + 0x0ed00103, + 0xf004bd00, +/* 0x081e: ctx_redswitch_delay */ + 0xf2b608f7, + 0xfd1bf401, + 0x0400e5f1, + 0x0100e5f1, + 0x850007f1, + 0xd00103f0, + 0x04bd000e, +/* 0x083a: ctx_86c */ + 0x07f100f8, + 0x03f02300, + 0x000fd002, + 0xffb904bd, + 0x14e7f102, + 0x40e3f08a, + 0xb99d21f4, + 0xe7f102ff, + 0xe3f0a88c, + 0x9d21f441, +/* 0x0862: ctx_mem */ + 0x07f100f8, + 0x03f08400, + 0x000fd002, +/* 0x086e: ctx_mem_wait */ + 0xf7f104bd, + 0xf3f08400, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f31b, +/* 0x0880: ctx_load */ + 0x99f094bd, + 0x0007f105, + 0x0203f037, + 0xbd0009d0, + 0x0ca7f004, + 0xbdd021f4, + 0x0007f1f4, + 0x0203f089, + 0xbd000fd0, + 0x0007f104, + 0x0203f0c1, + 0xbd0002d0, + 0x0007f104, + 0x0203f083, + 0xbd0002d0, + 0x07f7f004, + 0x086221f5, + 0xc00007f1, + 0xd00203f0, + 0x04bd0002, + 0xf0000bfe, + 0x24b61f2a, + 0x0220b604, + 0x99f094bd, + 0x0007f108, + 0x0203f037, + 0xbd0009d0, + 0x0007f104, + 0x0203f081, + 0xbd0002d0, + 0x0027f104, + 0x0023f100, + 0x0225f080, + 0x880007f1, + 0xd00203f0, + 0x04bd0002, + 0xf11017f0, + 0xf0020027, + 0x12fa0223, + 0xbd03f805, + 0x0899f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0xb6810198, + 0x02981814, + 0x0825b680, + 0x800512fd, + 0x94bd1601, + 0xf10999f0, + 0xf0370007, + 0x09d00203, + 0xf104bd00, + 0xf0810007, + 0x01d00203, + 0xf004bd00, + 0x07f10127, + 0x03f08800, + 0x0002d002, + 0x17f104bd, + 0x13f00100, + 0x0501fa06, + 0x94bd03f8, + 0xf10999f0, + 0xf0170007, + 0x09d00203, + 0xbd04bd00, + 0x0599f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x099e: ctx_chan */ + 0x21f500f8, + 0xa7f00880, + 0xd021f40c, + 0xf505f7f0, + 0xf8086221, +/* 0x09b1: ctx_mmio_exec */ + 0x41039800, + 0x810007f1, + 0xd00203f0, + 0x04bd0003, +/* 0x09c2: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x09d4: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b69d, + 0xf40112b6, +/* 0x09e6: ctx_mmio_done */ + 0x0398df1b, + 0x0007f116, + 0x0203f081, + 0xbd0003d0, + 0x40008004, + 0x010017f1, + 0xfa0613f0, + 0x03f80601, +/* 0x0a06: ctx_xfer */ + 0xe7f000f8, + 0x0007f104, + 0x0303f002, + 0xbd000ed0, +/* 0x0a15: ctx_xfer_idle */ + 0x00e7f104, + 0x03e3f000, + 0xf100eecf, + 0xf42000e4, + 0x11f4f21b, + 0x0d02f406, +/* 0x0a2c: ctx_xfer_pre */ + 0xf510f7f0, + 0xf4083a21, +/* 0x0a36: ctx_xfer_pre_load */ + 0xf7f01c11, + 0xdb21f502, + 0xed21f507, + 0x0221f507, + 0xf5f4bd08, + 0xf507db21, +/* 0x0a4f: ctx_xfer_exec */ + 0x98088021, + 0x24bd1601, + 0x050007f1, + 0xd00103f0, + 0x04bd0002, + 0xf1021fb9, + 0xf0a500e7, + 0x21f441e3, + 0x01fcf09d, + 0xb6022cf0, + 0xf2fd0124, + 0x02ffb905, + 0xa504e7f1, + 0xf441e3f0, + 0x21f59d21, + 0x24bd026a, + 0x47fc07f1, + 0xd00203f0, + 0x04bd0002, + 0xb6012cf0, + 0x07f10320, + 0x03f04afc, + 0x0002d002, + 0xacf004bd, + 0x06a5f001, + 0x9800b7f0, + 0x0d98000c, + 0x00e7f001, + 0x016f21f5, + 0xf508a7f0, + 0xf5011021, + 0xf4025e21, + 0xa7f01301, + 0xd021f40c, + 0xf505f7f0, + 0xf4086221, +/* 0x0ade: ctx_xfer_post */ + 0xf7f02e02, + 0xdb21f502, + 0xf5f4bd07, + 0xf5083a21, + 0xf5027f21, + 0xbd07ed21, + 0xdb21f5f4, + 0x1011f407, + 0xfd400198, + 0x0bf40511, + 0xb121f507, +/* 0x0b09: ctx_xfer_no_post_mmio */ +/* 0x0b09: ctx_xfer_done */ + 0x0000f809, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 new file mode 100644 index 000000000..43243a35f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5 @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#define CHIPSET GK208 +#include "macros.fuc" + +.section #gk208_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #gk208_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h new file mode 100644 index 000000000..ae2d5b689 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgk208.fuc5.h @@ -0,0 +1,918 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gk208_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +static uint32_t gk208_grhub_code[] = { + 0x030e0ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0xf489a408, + 0x020f0b1b, + 0x0002f87e, +/* 0x001a: queue_put_next */ + 0x98c400f8, + 0x0384b607, + 0xb6008dbb, + 0x8eb50880, + 0x018fb500, + 0xf00190b6, + 0xd9b50f94, +/* 0x0037: queue_get */ + 0xf400f801, + 0xd8980131, + 0x01d99800, + 0x0bf489a4, + 0x0789c421, + 0xbb0394b6, + 0x90b6009d, + 0x009e9808, + 0xb6019f98, + 0x84f00180, + 0x00d8b50f, +/* 0x0063: queue_get_done */ + 0xf80132f4, +/* 0x0065: nv_rd32 */ + 0xf0ecb200, + 0x00801fc9, + 0x0cf601ca, +/* 0x0073: nv_rd32_wait */ + 0x8c04bd00, + 0xcf01ca00, + 0xccc800cc, + 0xf61bf41f, + 0xec7e060a, + 0x008f0000, + 0xffcf01cb, +/* 0x008f: nv_wr32 */ + 0x8000f800, + 0xf601cc00, + 0x04bd000f, + 0xc9f0ecb2, + 0x1ec9f01f, + 0x01ca0080, + 0xbd000cf6, +/* 0x00a9: nv_wr32_wait */ + 0xca008c04, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f61b, +/* 0x00b8: wait_donez */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x00cf: wait_donez_ne */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf61bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x00ec: wait_doneo */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x0103: wait_doneo_e */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf60bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x0120: mmctx_size */ +/* 0x0122: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0x1bf4efa4, + 0xf89fb2ec, +/* 0x013d: mmctx_xfer */ + 0xf094bd00, + 0x00800199, + 0x09f60237, + 0xbd04bd00, + 0x05bbfd94, + 0x800f0bf4, + 0xf601c400, + 0x04bd000b, +/* 0x015f: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0xc6008018, + 0x000ef601, + 0x008004bd, + 0x0ff601c7, + 0xf004bd00, +/* 0x017a: mmctx_multi_disabled */ + 0xabc80199, + 0x10b4b600, + 0xc80cb9f0, + 0xe4b601ae, + 0x05befd11, + 0x01c50080, + 0xbd000bf6, +/* 0x0195: mmctx_exec_loop */ +/* 0x0195: mmctx_wait_free */ + 0xc5008e04, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f60b, + 0x05e9fd00, + 0x01c80080, + 0xbd000ef6, + 0x04c0b604, + 0x1bf4cda4, + 0x02abc8df, +/* 0x01bf: mmctx_fini_wait */ + 0x8b1c1bf4, + 0xcf01c500, + 0xb4f000bb, + 0x10b4b01f, + 0x0af31bf4, + 0x00b87e05, + 0x250ef400, +/* 0x01d8: mmctx_stop */ + 0xb600abc8, + 0xb9f010b4, + 0x12b9f00c, + 0x01c50080, + 0xbd000bf6, +/* 0x01ed: mmctx_stop_wait */ + 0xc5008b04, + 0x00bbcf01, + 0xf412bbc8, +/* 0x01fa: mmctx_done */ + 0x94bdf61b, + 0x800199f0, + 0xf6021700, + 0x04bd0009, +/* 0x020a: strand_wait */ + 0xa0f900f8, + 0xb87e020a, + 0xa0fc0000, +/* 0x0216: strand_pre */ + 0x0c0900f8, + 0x024afc80, + 0xbd0009f6, + 0x020a7e04, +/* 0x0227: strand_post */ + 0x0900f800, + 0x4afc800d, + 0x0009f602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0238: strand_set */ + 0xfc800f0c, + 0x0cf6024f, + 0x0c04bd00, + 0x4afc800b, + 0x000cf602, + 0xfc8004bd, + 0x0ef6024f, + 0x0c04bd00, + 0x4afc800a, + 0x000cf602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0268: strand_ctx_init */ + 0x99f094bd, + 0x37008003, + 0x0009f602, + 0x167e04bd, + 0x030e0002, + 0x0002387e, + 0xfc80c4bd, + 0x0cf60247, + 0x0c04bd00, + 0x4afc8001, + 0x000cf602, + 0x0a7e04bd, + 0x0c920002, + 0x46fc8001, + 0x000cf602, + 0x020c04bd, + 0x024afc80, + 0xbd000cf6, + 0x020a7e04, + 0x02277e00, + 0x42008800, + 0x20008902, + 0x0099cf02, +/* 0x02c7: ctx_init_strand_loop */ + 0xf608fe95, + 0x8ef6008e, + 0x808acf40, + 0xb606a5b6, + 0xeabb01a0, + 0x0480b600, + 0xf40192b6, + 0xe4b6e81b, + 0xf2efbc08, + 0x99f094bd, + 0x17008003, + 0x0009f602, + 0x00f804bd, +/* 0x02f8: error */ + 0x02050080, + 0xbd000ff6, + 0x80010f04, + 0xf6030700, + 0x04bd000f, +/* 0x030e: init */ + 0x04bd00f8, + 0x410007fe, + 0x11cf4200, + 0x0911e700, + 0x0814b601, + 0x020014fe, + 0x12004002, + 0xbd0002f6, + 0x05ca4104, + 0xbd0010fe, + 0x07004024, + 0xbd0002f6, + 0x20034204, + 0x01010080, + 0xbd0002f6, + 0x20044204, + 0x01010480, + 0xbd0002f6, + 0x200b4204, + 0x01010880, + 0xbd0002f6, + 0x200c4204, + 0x01011c80, + 0xbd0002f6, + 0x01039204, + 0x03090080, + 0xbd0003f6, + 0x87048204, + 0x04004000, + 0xbd0002f6, + 0x40040204, + 0x02f60300, + 0xf404bd00, + 0x048e1031, + 0x657e4096, + 0xfeb20000, + 0xb590f1c7, + 0xf4f00301, + 0x020fb51f, + 0x1fbb0101, + 0x0112b604, + 0x01030080, + 0xbd0001f6, + 0x04008004, + 0x0001f601, + 0x004104bd, + 0x7e020f01, + 0x7e0006ad, + 0x0f0006bc, + 0x06fe7e10, + 0x000e9800, + 0x7e010f98, + 0x95000120, + 0x00800814, + 0x04f601c0, + 0x8004bd00, + 0xf601c100, + 0x04bd0004, + 0x130030b7, + 0xb6001fbb, + 0x008002f5, + 0x0ff601d3, + 0xb604bd00, + 0x10b60815, + 0x0814b601, + 0x687e1fb2, + 0x1fbb0002, + 0x02039800, + 0x50200084, +/* 0x0420: init_gpc */ + 0x08044eb8, + 0x7e1fb200, + 0xb800008f, + 0x00010c4e, + 0x8f7ef4bd, + 0x4eb80000, + 0x7e000104, + 0xb800008f, + 0x0001004e, + 0x8f7e020f, + 0x4eb80000, +/* 0x044f: init_gpc_wait */ + 0x7e000800, + 0xc8000065, + 0x0bf41fff, + 0x044eb8f9, + 0x657e0008, + 0x1fbb0000, + 0x0040b700, + 0x0132b680, + 0x0fb41bf4, + 0x06fe7e00, + 0x7e000f00, + 0x800006ad, + 0xf6020100, + 0x04bd0001, + 0x19f014bd, + 0x3000801f, + 0x0001f602, +/* 0x0492: wait */ + 0x28f404bd, + 0x0031f400, +/* 0x0498: main */ + 0x377e100d, + 0x01f40000, + 0x01e4b1f4, + 0xc71bf540, + 0xf094bd00, + 0x00800499, + 0x09f60237, + 0x8104bd00, + 0xcf02c000, + 0x00820011, + 0x22cf02c1, + 0x1f13c800, + 0xc8770bf4, + 0x0bf41f23, + 0xb220f955, + 0xf094bd12, + 0x00800799, + 0x09f60237, + 0xf404bd00, + 0x31f40132, + 0x08817e02, + 0xf094bd00, + 0x00800799, + 0x09f60217, + 0xfc04bd00, + 0xf094bd20, + 0x00800699, + 0x09f60237, + 0xf404bd00, + 0x817e0131, + 0x94bd0008, + 0x800699f0, + 0xf6021700, + 0x04bd0009, +/* 0x0523: chsw_prev_no_next */ + 0xf92f0ef4, + 0xf412b220, + 0x32f40132, + 0x08817e02, + 0x8020fc00, + 0xf602c000, + 0x04bd0002, +/* 0x053f: chsw_no_prev */ + 0xc8130ef4, + 0x0bf41f23, + 0x0131f40d, + 0x7e0232f4, +/* 0x054f: chsw_done */ + 0x02000881, + 0xc3008001, + 0x0002f602, + 0x94bd04bd, + 0x800499f0, + 0xf6021700, + 0x04bd0009, + 0xff300ef5, +/* 0x056c: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b20c1b, + 0x0008217e, +/* 0x057b: main_not_ctx_chan */ + 0xb0400ef4, + 0x1bf402e4, + 0xf094bd2c, + 0x00800799, + 0x09f60237, + 0xf404bd00, + 0x32f40132, + 0x08817e02, + 0xf094bd00, + 0x00800799, + 0x09f60217, + 0xf404bd00, +/* 0x05aa: main_not_ctx_save */ + 0xef94110e, + 0x01f5f010, + 0x0002f87e, + 0xfee40ef5, +/* 0x05b8: main_done */ + 0x29f024bd, + 0x3000801f, + 0x0002f602, + 0x0ef504bd, +/* 0x05ca: ih */ + 0x00f9fed2, + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x02004a04, + 0xc400aacf, + 0x0bf404ab, + 0x4e100d23, + 0xeecf1a00, + 0x19004f00, + 0x7e00ffcf, + 0xb7000004, + 0x0e0400b0, + 0x1d004001, + 0xbd000ef6, +/* 0x060d: ih_no_fifo */ + 0x00abe404, + 0x0c0bf401, + 0x014e100d, + 0x00047e40, +/* 0x061d: ih_no_ctxsw */ + 0x00abe400, + 0x560bf404, + 0x4007088e, + 0x0000657e, + 0x0080ffb2, + 0x0ff60204, + 0x8e04bd00, + 0x7e400704, + 0xb2000065, + 0x030080ff, + 0x000ff602, + 0xfec704bd, + 0x02ee9450, + 0x4007008f, + 0x7e00efbb, + 0x80000065, + 0xf6020200, + 0x04bd000f, + 0xf87e030f, + 0x004b0002, + 0x8ebfb201, + 0x7e400144, +/* 0x0677: ih_no_fwmthd */ + 0x4b00008f, + 0xb0bd0504, + 0xf4b4abff, + 0x00800c0b, + 0x0bf60307, +/* 0x068b: ih_no_other */ + 0x4004bd00, + 0x0af60100, + 0xfc04bd00, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x00fc80fc, + 0xf80032f4, +/* 0x06ad: ctx_4170s */ + 0x10f5f001, + 0x708effb2, + 0x8f7e4041, + 0x00f80000, +/* 0x06bc: ctx_4170w */ + 0x4041708e, + 0x0000657e, + 0xf4f0ffb2, + 0xf31bf410, +/* 0x06ce: ctx_redswitch */ + 0x004e00f8, + 0x40e5f002, + 0xf020e5f0, + 0x008010e5, + 0x0ef60185, + 0x0f04bd00, +/* 0x06e5: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xf10400e5, + 0x800100e5, + 0xf6018500, + 0x04bd000e, +/* 0x06fe: ctx_86c */ + 0x008000f8, + 0x0ff60223, + 0xb204bd00, + 0x8a148eff, + 0x008f7e40, + 0x8effb200, + 0x7e41a88c, + 0xf800008f, +/* 0x071d: ctx_mem */ + 0x84008000, + 0x000ff602, +/* 0x0726: ctx_mem_wait */ + 0x008f04bd, + 0xffcf0284, + 0x05fffd00, + 0xf8f61bf4, +/* 0x0735: ctx_load */ + 0xf094bd00, + 0x00800599, + 0x09f60237, + 0x0a04bd00, + 0x00b87e0c, + 0x80f4bd00, + 0xf6028900, + 0x04bd000f, + 0x02c10080, + 0xbd0002f6, + 0x83008004, + 0x0002f602, + 0x070f04bd, + 0x00071d7e, + 0x02c00080, + 0xbd0002f6, + 0x000bfe04, + 0xb61f2af0, + 0x20b60424, + 0xf094bd02, + 0x00800899, + 0x09f60237, + 0x8004bd00, + 0xf6028100, + 0x04bd0002, + 0x000000d2, + 0x0225f080, + 0x02880080, + 0xbd0002f6, + 0x42100104, + 0x23f00200, + 0x0512fa02, + 0x94bd03f8, + 0x800899f0, + 0xf6021700, + 0x04bd0009, + 0xb6810198, + 0x02981814, + 0x0825b680, + 0xb50512fd, + 0x94bd1601, + 0x800999f0, + 0xf6023700, + 0x04bd0009, + 0x02810080, + 0xbd0001f6, + 0x80010204, + 0xf6028800, + 0x04bd0002, + 0xf0010041, + 0x01fa0613, + 0xbd03f805, + 0x0999f094, + 0x02170080, + 0xbd0009f6, + 0xf094bd04, + 0x00800599, + 0x09f60217, + 0xf804bd00, +/* 0x0821: ctx_chan */ + 0x07357e00, + 0x7e0c0a00, + 0x0f0000b8, + 0x071d7e05, +/* 0x0833: ctx_mmio_exec */ + 0x9800f800, + 0x00804103, + 0x03f60281, + 0xbd04bd00, +/* 0x0841: ctx_mmio_loop */ + 0xff34c434, + 0x450e1bf4, + 0x53f00200, + 0x0535fa06, +/* 0x0852: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0x00008f7e, + 0xb60830b6, + 0x1bf40112, +/* 0x0865: ctx_mmio_done */ + 0x160398df, + 0x02810080, + 0xbd0003f6, + 0x4000b504, + 0xf0010041, + 0x01fa0613, + 0xf803f806, +/* 0x0881: ctx_xfer */ + 0x80040e00, + 0xf6030200, + 0x04bd000e, +/* 0x088c: ctx_xfer_idle */ + 0x0300008e, + 0xf100eecf, + 0xf42000e4, + 0x11f4f51b, + 0x0c02f406, +/* 0x08a0: ctx_xfer_pre */ + 0xfe7e100f, + 0x11f40006, +/* 0x08a9: ctx_xfer_pre_load */ + 0x7e020f1b, + 0x7e0006ad, + 0x7e0006bc, + 0xbd0006ce, + 0x06ad7ef4, + 0x07357e00, +/* 0x08c1: ctx_xfer_exec */ + 0x16019800, + 0x008024bd, + 0x02f60105, + 0xb204bd00, + 0xa5008e1f, + 0x008f7e41, + 0x01fcf000, + 0xb6022cf0, + 0xf2fd0124, + 0x8effb205, + 0x7e41a504, + 0x7e00008f, + 0xbd000216, + 0x47fc8024, + 0x0002f602, + 0x2cf004bd, + 0x0320b601, + 0x024afc80, + 0xbd0002f6, + 0x01acf004, + 0x0b06a5f0, + 0x000c9800, + 0x0e010d98, + 0x013d7e00, + 0x7e080a00, + 0x7e0000ec, + 0xf400020a, + 0x0c0a1201, + 0x0000b87e, + 0x1d7e050f, + 0x02f40007, +/* 0x093d: ctx_xfer_post */ + 0x7e020f2d, + 0xbd0006ad, + 0x06fe7ef4, + 0x02277e00, + 0x06bc7e00, + 0x7ef4bd00, + 0xf40006ad, + 0x01981011, + 0x0511fd40, + 0x7e070bf4, +/* 0x0967: ctx_xfer_no_post_mmio */ +/* 0x0967: ctx_xfer_done */ + 0xf8000833, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 new file mode 100644 index 000000000..27591b308 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5 @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#define CHIPSET GK208 +#include "macros.fuc" + +.section #gm107_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #gm107_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h new file mode 100644 index 000000000..449dae753 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/hubgm107.fuc5.h @@ -0,0 +1,918 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gm107_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +static uint32_t gm107_grhub_code[] = { + 0x030e0ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0xf489a408, + 0x020f0b1b, + 0x0002f87e, +/* 0x001a: queue_put_next */ + 0x98c400f8, + 0x0384b607, + 0xb6008dbb, + 0x8eb50880, + 0x018fb500, + 0xf00190b6, + 0xd9b50f94, +/* 0x0037: queue_get */ + 0xf400f801, + 0xd8980131, + 0x01d99800, + 0x0bf489a4, + 0x0789c421, + 0xbb0394b6, + 0x90b6009d, + 0x009e9808, + 0xb6019f98, + 0x84f00180, + 0x00d8b50f, +/* 0x0063: queue_get_done */ + 0xf80132f4, +/* 0x0065: nv_rd32 */ + 0xf0ecb200, + 0x00801fc9, + 0x0cf601ca, +/* 0x0073: nv_rd32_wait */ + 0x8c04bd00, + 0xcf01ca00, + 0xccc800cc, + 0xf61bf41f, + 0xec7e060a, + 0x008f0000, + 0xffcf01cb, +/* 0x008f: nv_wr32 */ + 0x8000f800, + 0xf601cc00, + 0x04bd000f, + 0xc9f0ecb2, + 0x1ec9f01f, + 0x01ca0080, + 0xbd000cf6, +/* 0x00a9: nv_wr32_wait */ + 0xca008c04, + 0x00cccf01, + 0xf41fccc8, + 0x00f8f61b, +/* 0x00b8: wait_donez */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x00cf: wait_donez_ne */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf61bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x00ec: wait_doneo */ + 0x99f094bd, + 0x37008000, + 0x0009f602, + 0x008004bd, + 0x0af60206, +/* 0x0103: wait_doneo_e */ + 0x8804bd00, + 0xcf010000, + 0x8aff0088, + 0xf60bf488, + 0x99f094bd, + 0x17008000, + 0x0009f602, + 0x00f804bd, +/* 0x0120: mmctx_size */ +/* 0x0122: nv_mmctx_size_loop */ + 0xe89894bd, + 0x1a85b600, + 0xb60180b6, + 0x98bb0284, + 0x04e0b600, + 0x1bf4efa4, + 0xf89fb2ec, +/* 0x013d: mmctx_xfer */ + 0xf094bd00, + 0x00800199, + 0x09f60237, + 0xbd04bd00, + 0x05bbfd94, + 0x800f0bf4, + 0xf601c400, + 0x04bd000b, +/* 0x015f: mmctx_base_disabled */ + 0xfd0099f0, + 0x0bf405ee, + 0xc6008018, + 0x000ef601, + 0x008004bd, + 0x0ff601c7, + 0xf004bd00, +/* 0x017a: mmctx_multi_disabled */ + 0xabc80199, + 0x10b4b600, + 0xc80cb9f0, + 0xe4b601ae, + 0x05befd11, + 0x01c50080, + 0xbd000bf6, +/* 0x0195: mmctx_exec_loop */ +/* 0x0195: mmctx_wait_free */ + 0xc5008e04, + 0x00eecf01, + 0xf41fe4f0, + 0xce98f60b, + 0x05e9fd00, + 0x01c80080, + 0xbd000ef6, + 0x04c0b604, + 0x1bf4cda4, + 0x02abc8df, +/* 0x01bf: mmctx_fini_wait */ + 0x8b1c1bf4, + 0xcf01c500, + 0xb4f000bb, + 0x10b4b01f, + 0x0af31bf4, + 0x00b87e05, + 0x250ef400, +/* 0x01d8: mmctx_stop */ + 0xb600abc8, + 0xb9f010b4, + 0x12b9f00c, + 0x01c50080, + 0xbd000bf6, +/* 0x01ed: mmctx_stop_wait */ + 0xc5008b04, + 0x00bbcf01, + 0xf412bbc8, +/* 0x01fa: mmctx_done */ + 0x94bdf61b, + 0x800199f0, + 0xf6021700, + 0x04bd0009, +/* 0x020a: strand_wait */ + 0xa0f900f8, + 0xb87e020a, + 0xa0fc0000, +/* 0x0216: strand_pre */ + 0x0c0900f8, + 0x024afc80, + 0xbd0009f6, + 0x020a7e04, +/* 0x0227: strand_post */ + 0x0900f800, + 0x4afc800d, + 0x0009f602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0238: strand_set */ + 0xfc800f0c, + 0x0cf6024f, + 0x0c04bd00, + 0x4afc800b, + 0x000cf602, + 0xfc8004bd, + 0x0ef6024f, + 0x0c04bd00, + 0x4afc800a, + 0x000cf602, + 0x0a7e04bd, + 0x00f80002, +/* 0x0268: strand_ctx_init */ + 0x99f094bd, + 0x37008003, + 0x0009f602, + 0x167e04bd, + 0x030e0002, + 0x0002387e, + 0xfc80c4bd, + 0x0cf60247, + 0x0c04bd00, + 0x4afc8001, + 0x000cf602, + 0x0a7e04bd, + 0x0c920002, + 0x46fc8001, + 0x000cf602, + 0x020c04bd, + 0x024afc80, + 0xbd000cf6, + 0x020a7e04, + 0x02277e00, + 0x42008800, + 0x20008902, + 0x0099cf02, +/* 0x02c7: ctx_init_strand_loop */ + 0xf608fe95, + 0x8ef6008e, + 0x808acf40, + 0xb606a5b6, + 0xeabb01a0, + 0x0480b600, + 0xf40192b6, + 0xe4b6e81b, + 0xf2efbc08, + 0x99f094bd, + 0x17008003, + 0x0009f602, + 0x00f804bd, +/* 0x02f8: error */ + 0x02050080, + 0xbd000ff6, + 0x80010f04, + 0xf6030700, + 0x04bd000f, +/* 0x030e: init */ + 0x04bd00f8, + 0x410007fe, + 0x11cf4200, + 0x0911e700, + 0x0814b601, + 0x020014fe, + 0x12004002, + 0xbd0002f6, + 0x05ca4104, + 0xbd0010fe, + 0x07004024, + 0xbd0002f6, + 0x20034204, + 0x01010080, + 0xbd0002f6, + 0x20044204, + 0x01010480, + 0xbd0002f6, + 0x200b4204, + 0x01010880, + 0xbd0002f6, + 0x200c4204, + 0x01011c80, + 0xbd0002f6, + 0x01039204, + 0x03090080, + 0xbd0003f6, + 0x87048204, + 0x04004000, + 0xbd0002f6, + 0x40040204, + 0x02f60300, + 0xf404bd00, + 0x048e1031, + 0x657e4096, + 0xfeb20000, + 0xb590f1c7, + 0xf4f00301, + 0x020fb51f, + 0x1fbb0101, + 0x0112b604, + 0x01030080, + 0xbd0001f6, + 0x04008004, + 0x0001f601, + 0x004104bd, + 0x7e020f01, + 0x7e0006ad, + 0x0f0006bc, + 0x06fe7e10, + 0x000e9800, + 0x7e010f98, + 0x95000120, + 0x00800814, + 0x04f601c0, + 0x8004bd00, + 0xf601c100, + 0x04bd0004, + 0x130030b7, + 0xb6001fbb, + 0x008002f5, + 0x0ff601d3, + 0xb604bd00, + 0x10b60815, + 0x0814b601, + 0x687e1fb2, + 0x1fbb0002, + 0x02039800, + 0x50200084, +/* 0x0420: init_gpc */ + 0x08044eb8, + 0x7e1fb200, + 0xb800008f, + 0x00010c4e, + 0x8f7ef4bd, + 0x4eb80000, + 0x7e000104, + 0xb800008f, + 0x0001004e, + 0x8f7e020f, + 0x4eb80000, +/* 0x044f: init_gpc_wait */ + 0x7e000800, + 0xc8000065, + 0x0bf41fff, + 0x044eb8f9, + 0x657e0008, + 0x1fbb0000, + 0x0040b700, + 0x0132b680, + 0x0fb41bf4, + 0x06fe7e00, + 0x7e000f00, + 0x800006ad, + 0xf6020100, + 0x04bd0001, + 0x19f014bd, + 0x3000801f, + 0x0001f602, +/* 0x0492: wait */ + 0x28f404bd, + 0x0031f400, +/* 0x0498: main */ + 0x377e100d, + 0x01f40000, + 0x01e4b1f4, + 0xc71bf540, + 0xf094bd00, + 0x00800499, + 0x09f60237, + 0x8104bd00, + 0xcf02c000, + 0x00820011, + 0x22cf02c1, + 0x1f13c800, + 0xc8770bf4, + 0x0bf41f23, + 0xb220f955, + 0xf094bd12, + 0x00800799, + 0x09f60237, + 0xf404bd00, + 0x31f40132, + 0x08817e02, + 0xf094bd00, + 0x00800799, + 0x09f60217, + 0xfc04bd00, + 0xf094bd20, + 0x00800699, + 0x09f60237, + 0xf404bd00, + 0x817e0131, + 0x94bd0008, + 0x800699f0, + 0xf6021700, + 0x04bd0009, +/* 0x0523: chsw_prev_no_next */ + 0xf92f0ef4, + 0xf412b220, + 0x32f40132, + 0x08817e02, + 0x8020fc00, + 0xf602c000, + 0x04bd0002, +/* 0x053f: chsw_no_prev */ + 0xc8130ef4, + 0x0bf41f23, + 0x0131f40d, + 0x7e0232f4, +/* 0x054f: chsw_done */ + 0x02000881, + 0xc3008001, + 0x0002f602, + 0x94bd04bd, + 0x800499f0, + 0xf6021700, + 0x04bd0009, + 0xff300ef5, +/* 0x056c: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b20c1b, + 0x0008217e, +/* 0x057b: main_not_ctx_chan */ + 0xb0400ef4, + 0x1bf402e4, + 0xf094bd2c, + 0x00800799, + 0x09f60237, + 0xf404bd00, + 0x32f40132, + 0x08817e02, + 0xf094bd00, + 0x00800799, + 0x09f60217, + 0xf404bd00, +/* 0x05aa: main_not_ctx_save */ + 0xef94110e, + 0x01f5f010, + 0x0002f87e, + 0xfee40ef5, +/* 0x05b8: main_done */ + 0x29f024bd, + 0x3000801f, + 0x0002f602, + 0x0ef504bd, +/* 0x05ca: ih */ + 0x00f9fed2, + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x02004a04, + 0xc400aacf, + 0x0bf404ab, + 0x4e100d23, + 0xeecf1a00, + 0x19004f00, + 0x7e00ffcf, + 0xb7000004, + 0x0e0400b0, + 0x1d004001, + 0xbd000ef6, +/* 0x060d: ih_no_fifo */ + 0x00abe404, + 0x0c0bf401, + 0x014e100d, + 0x00047e40, +/* 0x061d: ih_no_ctxsw */ + 0x00abe400, + 0x560bf404, + 0x4007088e, + 0x0000657e, + 0x0080ffb2, + 0x0ff60204, + 0x8e04bd00, + 0x7e400704, + 0xb2000065, + 0x030080ff, + 0x000ff602, + 0xfec704bd, + 0x02ee9450, + 0x4007008f, + 0x7e00efbb, + 0x80000065, + 0xf6020200, + 0x04bd000f, + 0xf87e030f, + 0x004b0002, + 0x8ebfb201, + 0x7e400144, +/* 0x0677: ih_no_fwmthd */ + 0x4b00008f, + 0xb0bd0504, + 0xf4b4abff, + 0x00800c0b, + 0x0bf60307, +/* 0x068b: ih_no_other */ + 0x4004bd00, + 0x0af60100, + 0xfc04bd00, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x00fc80fc, + 0xf80032f4, +/* 0x06ad: ctx_4170s */ + 0x10f5f001, + 0x708effb2, + 0x8f7e4041, + 0x00f80000, +/* 0x06bc: ctx_4170w */ + 0x4041708e, + 0x0000657e, + 0xf4f0ffb2, + 0xf31bf410, +/* 0x06ce: ctx_redswitch */ + 0x004e00f8, + 0x40e5f002, + 0xf020e5f0, + 0x008010e5, + 0x0ef60185, + 0x0f04bd00, +/* 0x06e5: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xf10400e5, + 0x800100e5, + 0xf6018500, + 0x04bd000e, +/* 0x06fe: ctx_86c */ + 0x008000f8, + 0x0ff60223, + 0xb204bd00, + 0x8a148eff, + 0x008f7e40, + 0x8effb200, + 0x7e41a88c, + 0xf800008f, +/* 0x071d: ctx_mem */ + 0x84008000, + 0x000ff602, +/* 0x0726: ctx_mem_wait */ + 0x008f04bd, + 0xffcf0284, + 0x05fffd00, + 0xf8f61bf4, +/* 0x0735: ctx_load */ + 0xf094bd00, + 0x00800599, + 0x09f60237, + 0x0a04bd00, + 0x00b87e0c, + 0x80f4bd00, + 0xf6028900, + 0x04bd000f, + 0x02c10080, + 0xbd0002f6, + 0x83008004, + 0x0002f602, + 0x070f04bd, + 0x00071d7e, + 0x02c00080, + 0xbd0002f6, + 0x000bfe04, + 0xb61f2af0, + 0x20b60424, + 0xf094bd02, + 0x00800899, + 0x09f60237, + 0x8004bd00, + 0xf6028100, + 0x04bd0002, + 0x000000d2, + 0x0225f080, + 0x02880080, + 0xbd0002f6, + 0x42100104, + 0x23f00200, + 0x0512fa02, + 0x94bd03f8, + 0x800899f0, + 0xf6021700, + 0x04bd0009, + 0xb6810198, + 0x02981814, + 0x0825b680, + 0xb50512fd, + 0x94bd1601, + 0x800999f0, + 0xf6023700, + 0x04bd0009, + 0x02810080, + 0xbd0001f6, + 0x80010204, + 0xf6028800, + 0x04bd0002, + 0xf0010041, + 0x01fa0613, + 0xbd03f805, + 0x0999f094, + 0x02170080, + 0xbd0009f6, + 0xf094bd04, + 0x00800599, + 0x09f60217, + 0xf804bd00, +/* 0x0821: ctx_chan */ + 0x07357e00, + 0x7e0c0a00, + 0x0f0000b8, + 0x071d7e05, +/* 0x0833: ctx_mmio_exec */ + 0x9800f800, + 0x00804103, + 0x03f60281, + 0xbd04bd00, +/* 0x0841: ctx_mmio_loop */ + 0xff34c434, + 0x450e1bf4, + 0x53f00200, + 0x0535fa06, +/* 0x0852: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0x00008f7e, + 0xb60830b6, + 0x1bf40112, +/* 0x0865: ctx_mmio_done */ + 0x160398df, + 0x02810080, + 0xbd0003f6, + 0x4000b504, + 0xf0010041, + 0x01fa0613, + 0xf803f806, +/* 0x0881: ctx_xfer */ + 0x80040e00, + 0xf6030200, + 0x04bd000e, +/* 0x088c: ctx_xfer_idle */ + 0x0300008e, + 0xf100eecf, + 0xf42000e4, + 0x11f4f51b, + 0x0c02f406, +/* 0x08a0: ctx_xfer_pre */ + 0xfe7e100f, + 0x11f40006, +/* 0x08a9: ctx_xfer_pre_load */ + 0x7e020f1b, + 0x7e0006ad, + 0x7e0006bc, + 0xbd0006ce, + 0x06ad7ef4, + 0x07357e00, +/* 0x08c1: ctx_xfer_exec */ + 0x16019800, + 0x008024bd, + 0x02f60105, + 0xb204bd00, + 0xa5008e1f, + 0x008f7e41, + 0x01fcf000, + 0xb6022cf0, + 0xf2fd0124, + 0x8effb205, + 0x7e41a504, + 0x7e00008f, + 0xbd000216, + 0x47fc8024, + 0x0002f602, + 0x2cf004bd, + 0x0320b601, + 0x024afc80, + 0xbd0002f6, + 0x01acf004, + 0x0b06a5f0, + 0x000c9800, + 0x0e010d98, + 0x013d7e00, + 0x7e080a00, + 0x7e0000ec, + 0xf400020a, + 0x0c0a1201, + 0x0000b87e, + 0x1d7e050f, + 0x02f40007, +/* 0x093d: ctx_xfer_post */ + 0x7e020f2d, + 0xbd0006ad, + 0x06fe7ef4, + 0x02277e00, + 0x06bc7e00, + 0x7ef4bd00, + 0xf40006ad, + 0x01981011, + 0x0511fd40, + 0x7e070bf4, +/* 0x0967: ctx_xfer_no_post_mmio */ +/* 0x0967: ctx_xfer_done */ + 0xf8000833, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc new file mode 100644 index 000000000..fa6180664 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/macros.fuc @@ -0,0 +1,261 @@ +/* + * 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 "os.h" + +#define GF100 0xc0 +#define GF117 0xd7 +#define GK100 0xe0 +#define GK110 0xf0 +#define GK208 0x108 +#define GM107 0x117 + +#define NV_PGRAPH_TRAPPED_ADDR 0x400704 +#define NV_PGRAPH_TRAPPED_DATA_LO 0x400708 +#define NV_PGRAPH_TRAPPED_DATA_HI 0x40070c + +#define NV_PGRAPH_FE_OBJECT_TABLE(n) ((n) * 4 + 0x400700) + +#define NV_PGRAPH_FECS_INTR_ACK 0x409004 +#define NV_PGRAPH_FECS_INTR 0x409008 +#define NV_PGRAPH_FECS_INTR_FWMTHD 0x00000400 +#define NV_PGRAPH_FECS_INTR_CHSW 0x00000100 +#define NV_PGRAPH_FECS_INTR_FIFO 0x00000004 +#define NV_PGRAPH_FECS_INTR_MODE 0x40900c +#define NV_PGRAPH_FECS_INTR_MODE_FIFO 0x00000004 +#define NV_PGRAPH_FECS_INTR_MODE_FIFO_LEVEL 0x00000004 +#define NV_PGRAPH_FECS_INTR_MODE_FIFO_EDGE 0x00000000 +#define NV_PGRAPH_FECS_INTR_EN_SET 0x409010 +#define NV_PGRAPH_FECS_INTR_EN_SET_FIFO 0x00000004 +#define NV_PGRAPH_FECS_INTR_ROUTE 0x40901c +#define NV_PGRAPH_FECS_ACCESS 0x409048 +#define NV_PGRAPH_FECS_ACCESS_FIFO 0x00000002 +#define NV_PGRAPH_FECS_FIFO_DATA 0x409064 +#define NV_PGRAPH_FECS_FIFO_CMD 0x409068 +#define NV_PGRAPH_FECS_FIFO_ACK 0x409074 +#define NV_PGRAPH_FECS_CAPS 0x409108 +#define NV_PGRAPH_FECS_SIGNAL 0x409400 +#define NV_PGRAPH_FECS_IROUTE 0x409404 +#define NV_PGRAPH_FECS_BAR_MASK0 0x40940c +#define NV_PGRAPH_FECS_BAR_MASK1 0x409410 +#define NV_PGRAPH_FECS_BAR 0x409414 +#define NV_PGRAPH_FECS_BAR_SET 0x409418 +#define NV_PGRAPH_FECS_RED_SWITCH 0x409614 +#define NV_PGRAPH_FECS_RED_SWITCH_ENABLE_ROP 0x00000400 +#define NV_PGRAPH_FECS_RED_SWITCH_ENABLE_GPC 0x00000200 +#define NV_PGRAPH_FECS_RED_SWITCH_ENABLE_MAIN 0x00000100 +#define NV_PGRAPH_FECS_RED_SWITCH_POWER_ROP 0x00000040 +#define NV_PGRAPH_FECS_RED_SWITCH_POWER_GPC 0x00000020 +#define NV_PGRAPH_FECS_RED_SWITCH_POWER_MAIN 0x00000010 +#define NV_PGRAPH_FECS_RED_SWITCH_PAUSE_GPC 0x00000002 +#define NV_PGRAPH_FECS_RED_SWITCH_PAUSE_MAIN 0x00000001 +#define NV_PGRAPH_FECS_MMCTX_SAVE_SWBASE 0x409700 +#define NV_PGRAPH_FECS_MMCTX_LOAD_SWBASE 0x409704 +#define NV_PGRAPH_FECS_MMCTX_LOAD_COUNT 0x40974c +#define NV_PGRAPH_FECS_MMCTX_SAVE_SWBASE 0x409700 +#define NV_PGRAPH_FECS_MMCTX_LOAD_SWBASE 0x409704 +#define NV_PGRAPH_FECS_MMCTX_BASE 0x409710 +#define NV_PGRAPH_FECS_MMCTX_CTRL 0x409714 +#define NV_PGRAPH_FECS_MMCTX_MULTI_STRIDE 0x409718 +#define NV_PGRAPH_FECS_MMCTX_MULTI_MASK 0x40971c +#define NV_PGRAPH_FECS_MMCTX_QUEUE 0x409720 +#define NV_PGRAPH_FECS_MMIO_BASE 0x409724 +#define NV_PGRAPH_FECS_MMIO_CTRL 0x409728 +#define NV_PGRAPH_FECS_MMIO_CTRL_BASE_ENABLE 0x00000001 +#define NV_PGRAPH_FECS_MMIO_RDVAL 0x40972c +#define NV_PGRAPH_FECS_MMIO_WRVAL 0x409730 +#define NV_PGRAPH_FECS_MMCTX_LOAD_COUNT 0x40974c +#if CHIPSET < GK110 +#define NV_PGRAPH_FECS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x409800) +#define NV_PGRAPH_FECS_CC_SCRATCH_SET(n) ((n) * 4 + 0x409820) +#define NV_PGRAPH_FECS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x409840) +#define NV_PGRAPH_FECS_UNK86C 0x40986c +#else +#define NV_PGRAPH_FECS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x409800) +#define NV_PGRAPH_FECS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x409840) +#define NV_PGRAPH_FECS_UNK86C 0x40988c +#define NV_PGRAPH_FECS_CC_SCRATCH_SET(n) ((n) * 4 + 0x4098c0) +#endif +#define NV_PGRAPH_FECS_STRANDS_CNT 0x409880 +#define NV_PGRAPH_FECS_STRAND_SAVE_SWBASE 0x409908 +#define NV_PGRAPH_FECS_STRAND_LOAD_SWBASE 0x40990c +#define NV_PGRAPH_FECS_STRAND_WORDS 0x409910 +#define NV_PGRAPH_FECS_STRAND_DATA 0x409918 +#define NV_PGRAPH_FECS_STRAND_SELECT 0x40991c +#define NV_PGRAPH_FECS_STRAND_CMD 0x409928 +#define NV_PGRAPH_FECS_STRAND_CMD_SEEK 0x00000001 +#define NV_PGRAPH_FECS_STRAND_CMD_GET_INFO 0x00000002 +#define NV_PGRAPH_FECS_STRAND_CMD_SAVE 0x00000003 +#define NV_PGRAPH_FECS_STRAND_CMD_LOAD 0x00000004 +#define NV_PGRAPH_FECS_STRAND_CMD_ACTIVATE_FILTER 0x0000000a +#define NV_PGRAPH_FECS_STRAND_CMD_DEACTIVATE_FILTER 0x0000000b +#define NV_PGRAPH_FECS_STRAND_CMD_ENABLE 0x0000000c +#define NV_PGRAPH_FECS_STRAND_CMD_DISABLE 0x0000000d +#define NV_PGRAPH_FECS_STRAND_FILTER 0x40993c +#define NV_PGRAPH_FECS_MEM_BASE 0x409a04 +#define NV_PGRAPH_FECS_MEM_CHAN 0x409a0c +#define NV_PGRAPH_FECS_MEM_CMD 0x409a10 +#define NV_PGRAPH_FECS_MEM_CMD_LOAD_CHAN 0x00000007 +#define NV_PGRAPH_FECS_MEM_TARGET 0x409a20 +#define NV_PGRAPH_FECS_MEM_TARGET_UNK31 0x80000000 +#define NV_PGRAPH_FECS_MEM_TARGET_AS 0x0000001f +#define NV_PGRAPH_FECS_MEM_TARGET_AS_VM 0x00000001 +#define NV_PGRAPH_FECS_MEM_TARGET_AS_VRAM 0x00000002 +#define NV_PGRAPH_FECS_CHAN_ADDR 0x409b00 +#define NV_PGRAPH_FECS_CHAN_NEXT 0x409b04 +#define NV_PGRAPH_FECS_CHSW 0x409b0c +#define NV_PGRAPH_FECS_CHSW_ACK 0x00000001 +#define NV_PGRAPH_FECS_INTR_UP_SET 0x409c1c +#define NV_PGRAPH_FECS_INTR_UP_EN 0x409c24 + +#define NV_PGRAPH_GPCX_GPCCS_INTR_ACK 0x41a004 +#define NV_PGRAPH_GPCX_GPCCS_INTR 0x41a008 +#define NV_PGRAPH_GPCX_GPCCS_INTR_FIFO 0x00000004 +#define NV_PGRAPH_GPCX_GPCCS_INTR_EN_SET 0x41a010 +#define NV_PGRAPH_GPCX_GPCCS_INTR_EN_SET_FIFO 0x00000004 +#define NV_PGRAPH_GPCX_GPCCS_INTR_ROUTE 0x41a01c +#define NV_PGRAPH_GPCX_GPCCS_ACCESS 0x41a048 +#define NV_PGRAPH_GPCX_GPCCS_ACCESS_FIFO 0x00000002 +#define NV_PGRAPH_GPCX_GPCCS_FIFO_DATA 0x41a064 +#define NV_PGRAPH_GPCX_GPCCS_FIFO_CMD 0x41a068 +#define NV_PGRAPH_GPCX_GPCCS_FIFO_ACK 0x41a074 +#define NV_PGRAPH_GPCX_GPCCS_UNITS 0x41a608 +#define NV_PGRAPH_GPCX_GPCCS_CAPS 0x41a108 +#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH 0x41a614 +#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_UNK11 0x00000800 +#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_ENABLE 0x00000200 +#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_POWER 0x00000020 +#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_PAUSE 0x00000002 +#define NV_PGRAPH_GPCX_GPCCS_MYINDEX 0x41a618 +#define NV_PGRAPH_GPCX_GPCCS_MMCTX_SAVE_SWBASE 0x41a700 +#define NV_PGRAPH_GPCX_GPCCS_MMCTX_LOAD_SWBASE 0x41a704 +#define NV_PGRAPH_GPCX_GPCCS_MMIO_BASE 0x41a724 +#define NV_PGRAPH_GPCX_GPCCS_MMIO_CTRL 0x41a728 +#define NV_PGRAPH_GPCX_GPCCS_MMIO_CTRL_BASE_ENABLE 0x00000001 +#define NV_PGRAPH_GPCX_GPCCS_MMIO_RDVAL 0x41a72c +#define NV_PGRAPH_GPCX_GPCCS_MMIO_WRVAL 0x41a730 +#define NV_PGRAPH_GPCX_GPCCS_MMCTX_LOAD_COUNT 0x41a74c +#if CHIPSET < GK110 +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x41a800) +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(n) ((n) * 4 + 0x41a820) +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x41a840) +#define NV_PGRAPH_GPCX_GPCCS_UNK86C 0x41a86c +#else +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x41a800) +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x41a840) +#define NV_PGRAPH_GPCX_GPCCS_UNK86C 0x41a88c +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(n) ((n) * 4 + 0x41a8c0) +#endif +#define NV_PGRAPH_GPCX_GPCCS_STRAND_SELECT 0x41a91c +#define NV_PGRAPH_GPCX_GPCCS_STRAND_CMD 0x41a928 +#define NV_PGRAPH_GPCX_GPCCS_STRAND_CMD_SAVE 0x00000003 +#define NV_PGRAPH_GPCX_GPCCS_STRAND_CMD_LOAD 0x00000004 +#define NV_PGRAPH_GPCX_GPCCS_MEM_BASE 0x41aa04 +#define NV_PGRAPH_GPCX_GPCCS_TPC_STATUS 0x41acfc + +#define NV_PGRAPH_GPC0_TPC0 0x504000 +#define NV_PGRAPH_GPC0_TPC0__SIZE 0x000800 + +#define NV_PGRAPH_GPC0_TPCX_STRAND_INDEX 0x501d60 +#define NV_PGRAPH_GPC0_TPCX_STRAND_INDEX_ALL 0x0000003f +#define NV_PGRAPH_GPC0_TPCX_STRAND_DATA 0x501d98 +#define NV_PGRAPH_GPC0_TPCX_STRAND_SELECT 0x501d9c +#define NV_PGRAPH_GPC0_TPCX_STRAND_CMD 0x501da8 +#define NV_PGRAPH_GPC0_TPCX_STRAND_CMD_SEEK 0x00000001 +#define NV_PGRAPH_GPC0_TPCX_STRAND_CMD_GET_INFO 0x00000002 +#define NV_PGRAPH_GPC0_TPCX_STRAND_CMD_SAVE 0x00000003 +#define NV_PGRAPH_GPC0_TPCX_STRAND_CMD_LOAD 0x00000004 +#define NV_PGRAPH_GPC0_TPCX_STRAND_CMD_ENABLE 0x0000000c +#define NV_PGRAPH_GPC0_TPCX_STRAND_CMD_DISABLE 0x0000000d +#define NV_PGRAPH_GPC0_TPCX_STRAND_MEM_BASE 0x501dc4 + +#define NV_TPC_STRAND_INDEX 0x560 +#define NV_TPC_STRAND_CNT 0x570 +#define NV_TPC_STRAND_SAVE_SWBASE 0x588 +#define NV_TPC_STRAND_LOAD_SWBASE 0x58c +#define NV_TPC_STRAND_WORDS 0x590 + +#define mmctx_data(r,c) .b32 (((c - 1) << 26) | r) +#define queue_init .skip 72 // (2 * 4) + ((8 * 4) * 2) + +#define T_WAIT 0 +#define T_MMCTX 1 +#define T_STRWAIT 2 +#define T_STRINIT 3 +#define T_AUTO 4 +#define T_CHAN 5 +#define T_LOAD 6 +#define T_SAVE 7 +#define T_LCHAN 8 +#define T_LCTXH 9 +#define T_STRTPC 10 + +#if CHIPSET < GK208 +#define imm32(reg,val) /* +*/ movw reg ((val) & 0x0000ffff) /* +*/ sethi reg ((val) & 0xffff0000) +#else +#define imm32(reg,val) /* +*/ mov reg (val) +#endif + +#define nv_mkio(rv,r,i) /* +*/ imm32(rv, (((r) & 0xffc) << 6) | ((i) << 2)) + +#define hash # +#define fn(a) a +#if CHIPSET < GK208 +#define call(a) call fn(hash)a +#else +#define call(a) lcall fn(hash)a +#endif + +#define nv_iord(rv,r,i) /* +*/ nv_mkio(rv,r,i) /* +*/ iord rv I[rv] + +#define nv_iowr(r,i,rv) /* +*/ nv_mkio($r0,r,i) /* +*/ iowr I[$r0] rv /* +*/ clear b32 $r0 + +#define nv_rd32(reg,addr) /* +*/ imm32($r14, addr) /* +*/ call(nv_rd32) /* +*/ mov b32 reg $r15 + +#define nv_wr32(addr,reg) /* +*/ mov b32 $r15 reg /* +*/ imm32($r14, addr) /* +*/ call(nv_wr32) + +#define trace_set(bit) /* +*/ clear b32 $r9 /* +*/ bset $r9 bit /* +*/ nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(7), 0, $r9) + +#define trace_clr(bit) /* +*/ clear b32 $r9 /* +*/ bset $r9 bit /* +*/ nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_CLR(7), 0, $r9) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h new file mode 100644 index 000000000..6ac155b96 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/os.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_GRAPH_OS_H__ +#define __NVKM_GRAPH_OS_H__ + +#define E_BAD_COMMAND 0x00000001 +#define E_CMD_OVERFLOW 0x00000002 +#define E_BAD_FWMTHD 0x00000003 + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c new file mode 100644 index 000000000..65c332118 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c @@ -0,0 +1,198 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include + +#include + +static const struct nvkm_bitfield nv50_gr_status[] = { + { 0x00000001, "BUSY" }, /* set when any bit is set */ + { 0x00000002, "DISPATCH" }, + { 0x00000004, "UNK2" }, + { 0x00000008, "UNK3" }, + { 0x00000010, "UNK4" }, + { 0x00000020, "UNK5" }, + { 0x00000040, "M2MF" }, + { 0x00000080, "UNK7" }, + { 0x00000100, "CTXPROG" }, + { 0x00000200, "VFETCH" }, + { 0x00000400, "CCACHE_PREGEOM" }, + { 0x00000800, "STRMOUT_VATTR_POSTGEOM" }, + { 0x00001000, "VCLIP" }, + { 0x00002000, "RATTR_APLANE" }, + { 0x00004000, "TRAST" }, + { 0x00008000, "CLIPID" }, + { 0x00010000, "ZCULL" }, + { 0x00020000, "ENG2D" }, + { 0x00040000, "RMASK" }, + { 0x00080000, "TPC_RAST" }, + { 0x00100000, "TPC_PROP" }, + { 0x00200000, "TPC_TEX" }, + { 0x00400000, "TPC_GEOM" }, + { 0x00800000, "TPC_MP" }, + { 0x01000000, "ROP" }, + {} +}; + +static const struct nvkm_bitfield +nv50_gr_vstatus_0[] = { + { 0x01, "VFETCH" }, + { 0x02, "CCACHE" }, + { 0x04, "PREGEOM" }, + { 0x08, "POSTGEOM" }, + { 0x10, "VATTR" }, + { 0x20, "STRMOUT" }, + { 0x40, "VCLIP" }, + {} +}; + +static const struct nvkm_bitfield +nv50_gr_vstatus_1[] = { + { 0x01, "TPC_RAST" }, + { 0x02, "TPC_PROP" }, + { 0x04, "TPC_TEX" }, + { 0x08, "TPC_GEOM" }, + { 0x10, "TPC_MP" }, + {} +}; + +static const struct nvkm_bitfield +nv50_gr_vstatus_2[] = { + { 0x01, "RATTR" }, + { 0x02, "APLANE" }, + { 0x04, "TRAST" }, + { 0x08, "CLIPID" }, + { 0x10, "ZCULL" }, + { 0x20, "ENG2D" }, + { 0x40, "RMASK" }, + { 0x80, "ROP" }, + {} +}; + +static void +nvkm_gr_vstatus_print(struct nv50_gr *gr, int r, + const struct nvkm_bitfield *units, u32 status) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + u32 stat = status; + u8 mask = 0x00; + char msg[64]; + int i; + + for (i = 0; units[i].name && status; i++) { + if ((status & 7) == 1) + mask |= (1 << i); + status >>= 3; + } + + nvkm_snprintbf(msg, sizeof(msg), units, mask); + nvkm_error(subdev, "PGRAPH_VSTATUS%d: %08x [%s]\n", r, stat, msg); +} + +int +g84_gr_tlb_flush(struct nvkm_gr *base) +{ + struct nv50_gr *gr = nv50_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_timer *tmr = device->timer; + bool idle, timeout = false; + unsigned long flags; + char status[128]; + u64 start; + u32 tmp; + + spin_lock_irqsave(&gr->lock, flags); + nvkm_mask(device, 0x400500, 0x00000001, 0x00000000); + + start = nvkm_timer_read(tmr); + do { + idle = true; + + for (tmp = nvkm_rd32(device, 0x400380); tmp && idle; tmp >>= 3) { + if ((tmp & 7) == 1) + idle = false; + } + + for (tmp = nvkm_rd32(device, 0x400384); tmp && idle; tmp >>= 3) { + if ((tmp & 7) == 1) + idle = false; + } + + for (tmp = nvkm_rd32(device, 0x400388); tmp && idle; tmp >>= 3) { + if ((tmp & 7) == 1) + idle = false; + } + } while (!idle && + !(timeout = nvkm_timer_read(tmr) - start > 2000000000)); + + if (timeout) { + nvkm_error(subdev, "PGRAPH TLB flush idle timeout fail\n"); + + tmp = nvkm_rd32(device, 0x400700); + nvkm_snprintbf(status, sizeof(status), nv50_gr_status, tmp); + nvkm_error(subdev, "PGRAPH_STATUS %08x [%s]\n", tmp, status); + + nvkm_gr_vstatus_print(gr, 0, nv50_gr_vstatus_0, + nvkm_rd32(device, 0x400380)); + nvkm_gr_vstatus_print(gr, 1, nv50_gr_vstatus_1, + nvkm_rd32(device, 0x400384)); + nvkm_gr_vstatus_print(gr, 2, nv50_gr_vstatus_2, + nvkm_rd32(device, 0x400388)); + } + + + nvkm_wr32(device, 0x100c80, 0x00000001); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x100c80) & 0x00000001)) + break; + ); + nvkm_mask(device, 0x400500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&gr->lock, flags); + return timeout ? -EBUSY : 0; +} + +static const struct nvkm_gr_func +g84_gr = { + .init = nv50_gr_init, + .intr = nv50_gr_intr, + .chan_new = nv50_gr_chan_new, + .tlb_flush = g84_gr_tlb_flush, + .units = nv50_gr_units, + .sclass = { + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, G82_TESLA, &nv50_gr_object }, + {} + } +}; + +int +g84_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv50_gr_new_(&g84_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c new file mode 100644 index 000000000..f16eabf4f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -0,0 +1,2489 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" +#include "fuc/os.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/******************************************************************************* + * Zero Bandwidth Clear + ******************************************************************************/ + +static void +gf100_gr_zbc_clear_color(struct gf100_gr *gr, int zbc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + if (gr->zbc_color[zbc].format) { + nvkm_wr32(device, 0x405804, gr->zbc_color[zbc].ds[0]); + nvkm_wr32(device, 0x405808, gr->zbc_color[zbc].ds[1]); + nvkm_wr32(device, 0x40580c, gr->zbc_color[zbc].ds[2]); + nvkm_wr32(device, 0x405810, gr->zbc_color[zbc].ds[3]); + } + nvkm_wr32(device, 0x405814, gr->zbc_color[zbc].format); + nvkm_wr32(device, 0x405820, zbc); + nvkm_wr32(device, 0x405824, 0x00000004); /* TRIGGER | WRITE | COLOR */ +} + +static int +gf100_gr_zbc_color_get(struct gf100_gr *gr, int format, + const u32 ds[4], const u32 l2[4]) +{ + struct nvkm_ltc *ltc = gr->base.engine.subdev.device->ltc; + int zbc = -ENOSPC, i; + + for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) { + if (gr->zbc_color[i].format) { + if (gr->zbc_color[i].format != format) + continue; + if (memcmp(gr->zbc_color[i].ds, ds, sizeof( + gr->zbc_color[i].ds))) + continue; + if (memcmp(gr->zbc_color[i].l2, l2, sizeof( + gr->zbc_color[i].l2))) { + WARN_ON(1); + return -EINVAL; + } + return i; + } else { + zbc = (zbc < 0) ? i : zbc; + } + } + + if (zbc < 0) + return zbc; + + memcpy(gr->zbc_color[zbc].ds, ds, sizeof(gr->zbc_color[zbc].ds)); + memcpy(gr->zbc_color[zbc].l2, l2, sizeof(gr->zbc_color[zbc].l2)); + gr->zbc_color[zbc].format = format; + nvkm_ltc_zbc_color_get(ltc, zbc, l2); + gr->func->zbc->clear_color(gr, zbc); + return zbc; +} + +static void +gf100_gr_zbc_clear_depth(struct gf100_gr *gr, int zbc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + if (gr->zbc_depth[zbc].format) + nvkm_wr32(device, 0x405818, gr->zbc_depth[zbc].ds); + nvkm_wr32(device, 0x40581c, gr->zbc_depth[zbc].format); + nvkm_wr32(device, 0x405820, zbc); + nvkm_wr32(device, 0x405824, 0x00000005); /* TRIGGER | WRITE | DEPTH */ +} + +static int +gf100_gr_zbc_depth_get(struct gf100_gr *gr, int format, + const u32 ds, const u32 l2) +{ + struct nvkm_ltc *ltc = gr->base.engine.subdev.device->ltc; + int zbc = -ENOSPC, i; + + for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) { + if (gr->zbc_depth[i].format) { + if (gr->zbc_depth[i].format != format) + continue; + if (gr->zbc_depth[i].ds != ds) + continue; + if (gr->zbc_depth[i].l2 != l2) { + WARN_ON(1); + return -EINVAL; + } + return i; + } else { + zbc = (zbc < 0) ? i : zbc; + } + } + + if (zbc < 0) + return zbc; + + gr->zbc_depth[zbc].format = format; + gr->zbc_depth[zbc].ds = ds; + gr->zbc_depth[zbc].l2 = l2; + nvkm_ltc_zbc_depth_get(ltc, zbc, l2); + gr->func->zbc->clear_depth(gr, zbc); + return zbc; +} + +const struct gf100_gr_func_zbc +gf100_gr_zbc = { + .clear_color = gf100_gr_zbc_clear_color, + .clear_depth = gf100_gr_zbc_clear_depth, +}; + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ +#define gf100_gr_object(p) container_of((p), struct gf100_gr_object, object) + +struct gf100_gr_object { + struct nvkm_object object; + struct gf100_gr_chan *chan; +}; + +static int +gf100_fermi_mthd_zbc_color(struct nvkm_object *object, void *data, u32 size) +{ + struct gf100_gr *gr = gf100_gr(nvkm_gr(object->engine)); + union { + struct fermi_a_zbc_color_v0 v0; + } *args = data; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + switch (args->v0.format) { + case FERMI_A_ZBC_COLOR_V0_FMT_ZERO: + case FERMI_A_ZBC_COLOR_V0_FMT_UNORM_ONE: + case FERMI_A_ZBC_COLOR_V0_FMT_RF32_GF32_BF32_AF32: + case FERMI_A_ZBC_COLOR_V0_FMT_R16_G16_B16_A16: + case FERMI_A_ZBC_COLOR_V0_FMT_RN16_GN16_BN16_AN16: + case FERMI_A_ZBC_COLOR_V0_FMT_RS16_GS16_BS16_AS16: + case FERMI_A_ZBC_COLOR_V0_FMT_RU16_GU16_BU16_AU16: + case FERMI_A_ZBC_COLOR_V0_FMT_RF16_GF16_BF16_AF16: + case FERMI_A_ZBC_COLOR_V0_FMT_A8R8G8B8: + case FERMI_A_ZBC_COLOR_V0_FMT_A8RL8GL8BL8: + case FERMI_A_ZBC_COLOR_V0_FMT_A2B10G10R10: + case FERMI_A_ZBC_COLOR_V0_FMT_AU2BU10GU10RU10: + case FERMI_A_ZBC_COLOR_V0_FMT_A8B8G8R8: + case FERMI_A_ZBC_COLOR_V0_FMT_A8BL8GL8RL8: + case FERMI_A_ZBC_COLOR_V0_FMT_AN8BN8GN8RN8: + case FERMI_A_ZBC_COLOR_V0_FMT_AS8BS8GS8RS8: + case FERMI_A_ZBC_COLOR_V0_FMT_AU8BU8GU8RU8: + case FERMI_A_ZBC_COLOR_V0_FMT_A2R10G10B10: + case FERMI_A_ZBC_COLOR_V0_FMT_BF10GF11RF11: + ret = gf100_gr_zbc_color_get(gr, args->v0.format, + args->v0.ds, + args->v0.l2); + if (ret >= 0) { + args->v0.index = ret; + return 0; + } + break; + default: + return -EINVAL; + } + } + + return ret; +} + +static int +gf100_fermi_mthd_zbc_depth(struct nvkm_object *object, void *data, u32 size) +{ + struct gf100_gr *gr = gf100_gr(nvkm_gr(object->engine)); + union { + struct fermi_a_zbc_depth_v0 v0; + } *args = data; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + switch (args->v0.format) { + case FERMI_A_ZBC_DEPTH_V0_FMT_FP32: + ret = gf100_gr_zbc_depth_get(gr, args->v0.format, + args->v0.ds, + args->v0.l2); + return (ret >= 0) ? 0 : -ENOSPC; + default: + return -EINVAL; + } + } + + return ret; +} + +static int +gf100_fermi_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + nvif_ioctl(object, "fermi mthd %08x\n", mthd); + switch (mthd) { + case FERMI_A_ZBC_COLOR: + return gf100_fermi_mthd_zbc_color(object, data, size); + case FERMI_A_ZBC_DEPTH: + return gf100_fermi_mthd_zbc_depth(object, data, size); + default: + break; + } + return -EINVAL; +} + +const struct nvkm_object_func +gf100_fermi = { + .mthd = gf100_fermi_mthd, +}; + +static void +gf100_gr_mthd_set_shader_exceptions(struct nvkm_device *device, u32 data) +{ + nvkm_wr32(device, 0x419e44, data ? 0xffffffff : 0x00000000); + nvkm_wr32(device, 0x419e4c, data ? 0xffffffff : 0x00000000); +} + +static bool +gf100_gr_mthd_sw(struct nvkm_device *device, u16 class, u32 mthd, u32 data) +{ + switch (class & 0x00ff) { + case 0x97: + case 0xc0: + switch (mthd) { + case 0x1528: + gf100_gr_mthd_set_shader_exceptions(device, data); + return true; + default: + break; + } + break; + default: + break; + } + return false; +} + +static const struct nvkm_object_func +gf100_gr_object_func = { +}; + +static int +gf100_gr_object_new(const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct gf100_gr_chan *chan = gf100_gr_chan(oclass->parent); + struct gf100_gr_object *object; + + if (!(object = kzalloc(sizeof(*object), GFP_KERNEL))) + return -ENOMEM; + *pobject = &object->object; + + nvkm_object_ctor(oclass->base.func ? oclass->base.func : + &gf100_gr_object_func, oclass, &object->object); + object->chan = chan; + return 0; +} + +static int +gf100_gr_object_get(struct nvkm_gr *base, int index, struct nvkm_sclass *sclass) +{ + struct gf100_gr *gr = gf100_gr(base); + int c = 0; + + while (gr->func->sclass[c].oclass) { + if (c++ == index) { + *sclass = gr->func->sclass[index]; + sclass->ctor = gf100_gr_object_new; + return index; + } + } + + return c; +} + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +gf100_gr_chan_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct gf100_gr_chan *chan = gf100_gr_chan(object); + struct gf100_gr *gr = chan->gr; + int ret, i; + + ret = nvkm_gpuobj_new(gr->base.engine.subdev.device, gr->size, + align, false, parent, pgpuobj); + if (ret) + return ret; + + nvkm_kmap(*pgpuobj); + for (i = 0; i < gr->size; i += 4) + nvkm_wo32(*pgpuobj, i, gr->data[i / 4]); + + if (!gr->firmware) { + nvkm_wo32(*pgpuobj, 0x00, chan->mmio_nr / 2); + nvkm_wo32(*pgpuobj, 0x04, chan->mmio_vma->addr >> 8); + } else { + nvkm_wo32(*pgpuobj, 0xf4, 0); + nvkm_wo32(*pgpuobj, 0xf8, 0); + nvkm_wo32(*pgpuobj, 0x10, chan->mmio_nr / 2); + nvkm_wo32(*pgpuobj, 0x14, lower_32_bits(chan->mmio_vma->addr)); + nvkm_wo32(*pgpuobj, 0x18, upper_32_bits(chan->mmio_vma->addr)); + nvkm_wo32(*pgpuobj, 0x1c, 1); + nvkm_wo32(*pgpuobj, 0x20, 0); + nvkm_wo32(*pgpuobj, 0x28, 0); + nvkm_wo32(*pgpuobj, 0x2c, 0); + } + nvkm_done(*pgpuobj); + return 0; +} + +static void * +gf100_gr_chan_dtor(struct nvkm_object *object) +{ + struct gf100_gr_chan *chan = gf100_gr_chan(object); + int i; + + for (i = 0; i < ARRAY_SIZE(chan->data); i++) { + nvkm_vmm_put(chan->vmm, &chan->data[i].vma); + nvkm_memory_unref(&chan->data[i].mem); + } + + nvkm_vmm_put(chan->vmm, &chan->mmio_vma); + nvkm_memory_unref(&chan->mmio); + nvkm_vmm_unref(&chan->vmm); + return chan; +} + +static const struct nvkm_object_func +gf100_gr_chan = { + .dtor = gf100_gr_chan_dtor, + .bind = gf100_gr_chan_bind, +}; + +static int +gf100_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct gf100_gr *gr = gf100_gr(base); + struct gf100_gr_data *data = gr->mmio_data; + struct gf100_gr_mmio *mmio = gr->mmio_list; + struct gf100_gr_chan *chan; + struct gf100_vmm_map_v0 args = { .priv = 1 }; + struct nvkm_device *device = gr->base.engine.subdev.device; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&gf100_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->vmm = nvkm_vmm_ref(fifoch->vmm); + *pobject = &chan->object; + + /* allocate memory for a "mmio list" buffer that's used by the HUB + * fuc to modify some per-context register settings on first load + * of the context. + */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0x100, + false, &chan->mmio); + if (ret) + return ret; + + ret = nvkm_vmm_get(fifoch->vmm, 12, 0x1000, &chan->mmio_vma); + if (ret) + return ret; + + ret = nvkm_memory_map(chan->mmio, 0, fifoch->vmm, + chan->mmio_vma, &args, sizeof(args)); + if (ret) + return ret; + + /* allocate buffers referenced by mmio list */ + for (i = 0; data->size && i < ARRAY_SIZE(gr->mmio_data); i++) { + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, + data->size, data->align, false, + &chan->data[i].mem); + if (ret) + return ret; + + ret = nvkm_vmm_get(fifoch->vmm, 12, + nvkm_memory_size(chan->data[i].mem), + &chan->data[i].vma); + if (ret) + return ret; + + args.priv = data->priv; + + ret = nvkm_memory_map(chan->data[i].mem, 0, chan->vmm, + chan->data[i].vma, &args, sizeof(args)); + if (ret) + return ret; + + data++; + } + + /* finally, fill in the mmio list and point the context at it */ + nvkm_kmap(chan->mmio); + for (i = 0; mmio->addr && i < ARRAY_SIZE(gr->mmio_list); i++) { + u32 addr = mmio->addr; + u32 data = mmio->data; + + if (mmio->buffer >= 0) { + u64 info = chan->data[mmio->buffer].vma->addr; + data |= info >> mmio->shift; + } + + nvkm_wo32(chan->mmio, chan->mmio_nr++ * 4, addr); + nvkm_wo32(chan->mmio, chan->mmio_nr++ * 4, data); + mmio++; + } + nvkm_done(chan->mmio); + return 0; +} + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +const struct gf100_gr_init +gf100_gr_init_main_0[] = { + { 0x400080, 1, 0x04, 0x003083c2 }, + { 0x400088, 1, 0x04, 0x00006fe7 }, + { 0x40008c, 1, 0x04, 0x00000000 }, + { 0x400090, 1, 0x04, 0x00000030 }, + { 0x40013c, 1, 0x04, 0x013901f7 }, + { 0x400140, 1, 0x04, 0x00000100 }, + { 0x400144, 1, 0x04, 0x00000000 }, + { 0x400148, 1, 0x04, 0x00000110 }, + { 0x400138, 1, 0x04, 0x00000000 }, + { 0x400130, 2, 0x04, 0x00000000 }, + { 0x400124, 1, 0x04, 0x00000002 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_fe_0[] = { + { 0x40415c, 1, 0x04, 0x00000000 }, + { 0x404170, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_pri_0[] = { + { 0x404488, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_rstr2d_0[] = { + { 0x407808, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_pd_0[] = { + { 0x406024, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_ds_0[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405908, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_scc_0[] = { + { 0x40803c, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_prop_0[] = { + { 0x4184a0, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_gpc_unk_0[] = { + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x80000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_setup_0[] = { + { 0x418814, 3, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_crstr_0[] = { + { 0x418b04, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_setup_1[] = { + { 0x4188c8, 1, 0x04, 0x80000000 }, + { 0x4188cc, 1, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_zcull_0[] = { + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_gpm_0[] = { + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_gpc_unk_1[] = { + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000050 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_gcc_0[] = { + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_tpccs_0[] = { + { 0x419d08, 2, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_tex_0[] = { + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_pe_0[] = { + { 0x41980c, 3, 0x04, 0x00000000 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc5 }, + { 0x419850, 4, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_l1c_0[] = { + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x80000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00008bf4 }, + { 0x419cbc, 1, 0x04, 0x28137606 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_wwdx_0[] = { + { 0x419bd4, 1, 0x04, 0x00800000 }, + { 0x419bdc, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_tpccs_1[] = { + { 0x419d2c, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_mpc_0[] = { + { 0x419c0c, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf100_gr_init_sm_0[] = { + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x06060618 }, + { 0x419ed0, 1, 0x04, 0x0eff0e38 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_be_0[] = { + { 0x40880c, 1, 0x04, 0x00000000 }, + { 0x408910, 9, 0x04, 0x00000000 }, + { 0x408950, 1, 0x04, 0x00000000 }, + { 0x408954, 1, 0x04, 0x0000ffff }, + { 0x408984, 1, 0x04, 0x00000000 }, + { 0x408988, 1, 0x04, 0x08040201 }, + { 0x40898c, 1, 0x04, 0x80402010 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_fe_1[] = { + { 0x4040f0, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf100_gr_init_pe_1[] = { + { 0x419880, 1, 0x04, 0x00000002 }, + {} +}; + +static const struct gf100_gr_pack +gf100_gr_pack_mmio[] = { + { gf100_gr_init_main_0 }, + { gf100_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf100_gr_init_pd_0 }, + { gf100_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gf100_gr_init_prop_0 }, + { gf100_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf100_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf100_gr_init_gpm_0 }, + { gf100_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gf100_gr_init_tpccs_0 }, + { gf100_gr_init_tex_0 }, + { gf100_gr_init_pe_0 }, + { gf100_gr_init_l1c_0 }, + { gf100_gr_init_wwdx_0 }, + { gf100_gr_init_tpccs_1 }, + { gf100_gr_init_mpc_0 }, + { gf100_gr_init_sm_0 }, + { gf100_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + { gf100_gr_init_pe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static u32 +gf100_gr_ctxsw_inst(struct nvkm_gr *gr) +{ + return nvkm_rd32(gr->engine.subdev.device, 0x409b00); +} + +static int +gf100_gr_fecs_ctrl_ctxsw(struct gf100_gr *gr, u32 mthd) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409804, 0xffffffff); + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0xffffffff); + nvkm_wr32(device, 0x409504, mthd); + nvkm_msec(device, 2000, + u32 stat = nvkm_rd32(device, 0x409804); + if (stat == 0x00000002) + return -EIO; + if (stat == 0x00000001) + return 0; + ); + + return -ETIMEDOUT; +} + +static int +gf100_gr_fecs_start_ctxsw(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + int ret = 0; + + mutex_lock(&gr->fecs.mutex); + if (!--gr->fecs.disable) { + if (WARN_ON(ret = gf100_gr_fecs_ctrl_ctxsw(gr, 0x39))) + gr->fecs.disable++; + } + mutex_unlock(&gr->fecs.mutex); + return ret; +} + +static int +gf100_gr_fecs_stop_ctxsw(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + int ret = 0; + + mutex_lock(&gr->fecs.mutex); + if (!gr->fecs.disable++) { + if (WARN_ON(ret = gf100_gr_fecs_ctrl_ctxsw(gr, 0x38))) + gr->fecs.disable--; + } + mutex_unlock(&gr->fecs.mutex); + return ret; +} + +int +gf100_gr_fecs_bind_pointer(struct gf100_gr *gr, u32 inst) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409840, 0x00000030); + nvkm_wr32(device, 0x409500, inst); + nvkm_wr32(device, 0x409504, 0x00000003); + nvkm_msec(device, 2000, + u32 stat = nvkm_rd32(device, 0x409800); + if (stat & 0x00000020) + return -EIO; + if (stat & 0x00000010) + return 0; + ); + + return -ETIMEDOUT; +} + +static int +gf100_gr_fecs_set_reglist_virtual_address(struct gf100_gr *gr, u64 addr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409810, addr >> 8); + nvkm_wr32(device, 0x409800, 0x00000000); + nvkm_wr32(device, 0x409500, 0x00000001); + nvkm_wr32(device, 0x409504, 0x00000032); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800) == 0x00000001) + return 0; + ); + + return -ETIMEDOUT; +} + +static int +gf100_gr_fecs_set_reglist_bind_instance(struct gf100_gr *gr, u32 inst) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409810, inst); + nvkm_wr32(device, 0x409800, 0x00000000); + nvkm_wr32(device, 0x409500, 0x00000001); + nvkm_wr32(device, 0x409504, 0x00000031); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800) == 0x00000001) + return 0; + ); + + return -ETIMEDOUT; +} + +static int +gf100_gr_fecs_discover_reglist_image_size(struct gf100_gr *gr, u32 *psize) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409800, 0x00000000); + nvkm_wr32(device, 0x409500, 0x00000001); + nvkm_wr32(device, 0x409504, 0x00000030); + nvkm_msec(device, 2000, + if ((*psize = nvkm_rd32(device, 0x409800))) + return 0; + ); + + return -ETIMEDOUT; +} + +static int +gf100_gr_fecs_elpg_bind(struct gf100_gr *gr) +{ + u32 size; + int ret; + + ret = gf100_gr_fecs_discover_reglist_image_size(gr, &size); + if (ret) + return ret; + + /*XXX: We need to allocate + map the above into PMU's inst block, + * which which means we probably need a proper PMU before we + * even bother. + */ + + ret = gf100_gr_fecs_set_reglist_bind_instance(gr, 0); + if (ret) + return ret; + + return gf100_gr_fecs_set_reglist_virtual_address(gr, 0); +} + +static int +gf100_gr_fecs_discover_pm_image_size(struct gf100_gr *gr, u32 *psize) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0x00000000); + nvkm_wr32(device, 0x409504, 0x00000025); + nvkm_msec(device, 2000, + if ((*psize = nvkm_rd32(device, 0x409800))) + return 0; + ); + + return -ETIMEDOUT; +} + +static int +gf100_gr_fecs_discover_zcull_image_size(struct gf100_gr *gr, u32 *psize) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0x00000000); + nvkm_wr32(device, 0x409504, 0x00000016); + nvkm_msec(device, 2000, + if ((*psize = nvkm_rd32(device, 0x409800))) + return 0; + ); + + return -ETIMEDOUT; +} + +static int +gf100_gr_fecs_discover_image_size(struct gf100_gr *gr, u32 *psize) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0x00000000); + nvkm_wr32(device, 0x409504, 0x00000010); + nvkm_msec(device, 2000, + if ((*psize = nvkm_rd32(device, 0x409800))) + return 0; + ); + + return -ETIMEDOUT; +} + +static void +gf100_gr_fecs_set_watchdog_timeout(struct gf100_gr *gr, u32 timeout) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, timeout); + nvkm_wr32(device, 0x409504, 0x00000021); +} + +static bool +gf100_gr_chsw_load(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + if (!gr->firmware) { + u32 trace = nvkm_rd32(gr->base.engine.subdev.device, 0x40981c); + if (trace & 0x00000040) + return true; + } else { + u32 mthd = nvkm_rd32(gr->base.engine.subdev.device, 0x409808); + if (mthd & 0x00080000) + return true; + } + return false; +} + +int +gf100_gr_rops(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + return (nvkm_rd32(device, 0x409604) & 0x001f0000) >> 16; +} + +void +gf100_gr_zbc_init(struct gf100_gr *gr) +{ + const u32 zero[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + const u32 one[] = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; + const u32 f32_0[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + const u32 f32_1[] = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, + 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 }; + struct nvkm_ltc *ltc = gr->base.engine.subdev.device->ltc; + int index, c = ltc->zbc_min, d = ltc->zbc_min, s = ltc->zbc_min; + + if (!gr->zbc_color[0].format) { + gf100_gr_zbc_color_get(gr, 1, & zero[0], &zero[4]); c++; + gf100_gr_zbc_color_get(gr, 2, & one[0], &one[4]); c++; + gf100_gr_zbc_color_get(gr, 4, &f32_0[0], &f32_0[4]); c++; + gf100_gr_zbc_color_get(gr, 4, &f32_1[0], &f32_1[4]); c++; + gf100_gr_zbc_depth_get(gr, 1, 0x00000000, 0x00000000); d++; + gf100_gr_zbc_depth_get(gr, 1, 0x3f800000, 0x3f800000); d++; + if (gr->func->zbc->stencil_get) { + gr->func->zbc->stencil_get(gr, 1, 0x00, 0x00); s++; + gr->func->zbc->stencil_get(gr, 1, 0x01, 0x01); s++; + gr->func->zbc->stencil_get(gr, 1, 0xff, 0xff); s++; + } + } + + for (index = c; index <= ltc->zbc_max; index++) + gr->func->zbc->clear_color(gr, index); + for (index = d; index <= ltc->zbc_max; index++) + gr->func->zbc->clear_depth(gr, index); + + if (gr->func->zbc->clear_stencil) { + for (index = s; index <= ltc->zbc_max; index++) + gr->func->zbc->clear_stencil(gr, index); + } +} + +/** + * Wait until GR goes idle. GR is considered idle if it is disabled by the + * MC (0x200) register, or GR is not busy and a context switch is not in + * progress. + */ +int +gf100_gr_wait_idle(struct gf100_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + unsigned long end_jiffies = jiffies + msecs_to_jiffies(2000); + bool gr_enabled, ctxsw_active, gr_busy; + + do { + /* + * required to make sure FIFO_ENGINE_STATUS (0x2640) is + * up-to-date + */ + nvkm_rd32(device, 0x400700); + + gr_enabled = nvkm_rd32(device, 0x200) & 0x1000; + ctxsw_active = nvkm_rd32(device, 0x2640) & 0x8000; + gr_busy = nvkm_rd32(device, 0x40060c) & 0x1; + + if (!gr_enabled || (!gr_busy && !ctxsw_active)) + return 0; + } while (time_before(jiffies, end_jiffies)); + + nvkm_error(subdev, + "wait for idle timeout (en: %d, ctxsw: %d, busy: %d)\n", + gr_enabled, ctxsw_active, gr_busy); + return -EAGAIN; +} + +void +gf100_gr_mmio(struct gf100_gr *gr, const struct gf100_gr_pack *p) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const struct gf100_gr_pack *pack; + const struct gf100_gr_init *init; + + pack_for_each_init(init, pack, p) { + u32 next = init->addr + init->count * init->pitch; + u32 addr = init->addr; + while (addr < next) { + nvkm_wr32(device, addr, init->data); + addr += init->pitch; + } + } +} + +void +gf100_gr_icmd(struct gf100_gr *gr, const struct gf100_gr_pack *p) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const struct gf100_gr_pack *pack; + const struct gf100_gr_init *init; + u32 data = 0; + + nvkm_wr32(device, 0x400208, 0x80000000); + + pack_for_each_init(init, pack, p) { + u32 next = init->addr + init->count * init->pitch; + u32 addr = init->addr; + + if ((pack == p && init == p->init) || data != init->data) { + nvkm_wr32(device, 0x400204, init->data); + data = init->data; + } + + while (addr < next) { + nvkm_wr32(device, 0x400200, addr); + /** + * Wait for GR to go idle after submitting a + * GO_IDLE bundle + */ + if ((addr & 0xffff) == 0xe100) + gf100_gr_wait_idle(gr); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x400700) & 0x00000004)) + break; + ); + addr += init->pitch; + } + } + + nvkm_wr32(device, 0x400208, 0x00000000); +} + +void +gf100_gr_mthd(struct gf100_gr *gr, const struct gf100_gr_pack *p) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const struct gf100_gr_pack *pack; + const struct gf100_gr_init *init; + u32 data = 0; + + pack_for_each_init(init, pack, p) { + u32 ctrl = 0x80000000 | pack->type; + u32 next = init->addr + init->count * init->pitch; + u32 addr = init->addr; + + if ((pack == p && init == p->init) || data != init->data) { + nvkm_wr32(device, 0x40448c, init->data); + data = init->data; + } + + while (addr < next) { + nvkm_wr32(device, 0x404488, ctrl | (addr << 14)); + addr += init->pitch; + } + } +} + +u64 +gf100_gr_units(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + u64 cfg; + + cfg = (u32)gr->gpc_nr; + cfg |= (u32)gr->tpc_total << 8; + cfg |= (u64)gr->rop_nr << 32; + + return cfg; +} + +static const struct nvkm_bitfield gf100_dispatch_error[] = { + { 0x00000001, "INJECTED_BUNDLE_ERROR" }, + { 0x00000002, "CLASS_SUBCH_MISMATCH" }, + { 0x00000004, "SUBCHSW_DURING_NOTIFY" }, + {} +}; + +static const struct nvkm_bitfield gf100_m2mf_error[] = { + { 0x00000001, "PUSH_TOO_MUCH_DATA" }, + { 0x00000002, "PUSH_NOT_ENOUGH_DATA" }, + {} +}; + +static const struct nvkm_bitfield gf100_unk6_error[] = { + { 0x00000001, "TEMP_TOO_SMALL" }, + {} +}; + +static const struct nvkm_bitfield gf100_ccache_error[] = { + { 0x00000001, "INTR" }, + { 0x00000002, "LDCONST_OOB" }, + {} +}; + +static const struct nvkm_bitfield gf100_macro_error[] = { + { 0x00000001, "TOO_FEW_PARAMS" }, + { 0x00000002, "TOO_MANY_PARAMS" }, + { 0x00000004, "ILLEGAL_OPCODE" }, + { 0x00000008, "DOUBLE_BRANCH" }, + { 0x00000010, "WATCHDOG" }, + {} +}; + +static const struct nvkm_bitfield gk104_sked_error[] = { + { 0x00000040, "CTA_RESUME" }, + { 0x00000080, "CONSTANT_BUFFER_SIZE" }, + { 0x00000200, "LOCAL_MEMORY_SIZE_POS" }, + { 0x00000400, "LOCAL_MEMORY_SIZE_NEG" }, + { 0x00000800, "WARP_CSTACK_SIZE" }, + { 0x00001000, "TOTAL_TEMP_SIZE" }, + { 0x00002000, "REGISTER_COUNT" }, + { 0x00040000, "TOTAL_THREADS" }, + { 0x00100000, "PROGRAM_OFFSET" }, + { 0x00200000, "SHARED_MEMORY_SIZE" }, + { 0x00800000, "CTA_THREAD_DIMENSION_ZERO" }, + { 0x01000000, "MEMORY_WINDOW_OVERLAP" }, + { 0x02000000, "SHARED_CONFIG_TOO_SMALL" }, + { 0x04000000, "TOTAL_REGISTER_COUNT" }, + {} +}; + +static const struct nvkm_bitfield gf100_gpc_rop_error[] = { + { 0x00000002, "RT_PITCH_OVERRUN" }, + { 0x00000010, "RT_WIDTH_OVERRUN" }, + { 0x00000020, "RT_HEIGHT_OVERRUN" }, + { 0x00000080, "ZETA_STORAGE_TYPE_MISMATCH" }, + { 0x00000100, "RT_STORAGE_TYPE_MISMATCH" }, + { 0x00000400, "RT_LINEAR_MISMATCH" }, + {} +}; + +static void +gf100_gr_trap_gpc_rop(struct gf100_gr *gr, int gpc) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + char error[128]; + u32 trap[4]; + + trap[0] = nvkm_rd32(device, GPC_UNIT(gpc, 0x0420)) & 0x3fffffff; + trap[1] = nvkm_rd32(device, GPC_UNIT(gpc, 0x0434)); + trap[2] = nvkm_rd32(device, GPC_UNIT(gpc, 0x0438)); + trap[3] = nvkm_rd32(device, GPC_UNIT(gpc, 0x043c)); + + nvkm_snprintbf(error, sizeof(error), gf100_gpc_rop_error, trap[0]); + + nvkm_error(subdev, "GPC%d/PROP trap: %08x [%s] x = %u, y = %u, " + "format = %x, storage type = %x\n", + gpc, trap[0], error, trap[1] & 0xffff, trap[1] >> 16, + (trap[2] >> 8) & 0x3f, trap[3] & 0xff); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0420), 0xc0000000); +} + +const struct nvkm_enum gf100_mp_warp_error[] = { + { 0x01, "STACK_ERROR" }, + { 0x02, "API_STACK_ERROR" }, + { 0x03, "RET_EMPTY_STACK_ERROR" }, + { 0x04, "PC_WRAP" }, + { 0x05, "MISALIGNED_PC" }, + { 0x06, "PC_OVERFLOW" }, + { 0x07, "MISALIGNED_IMMC_ADDR" }, + { 0x08, "MISALIGNED_REG" }, + { 0x09, "ILLEGAL_INSTR_ENCODING" }, + { 0x0a, "ILLEGAL_SPH_INSTR_COMBO" }, + { 0x0b, "ILLEGAL_INSTR_PARAM" }, + { 0x0c, "INVALID_CONST_ADDR" }, + { 0x0d, "OOR_REG" }, + { 0x0e, "OOR_ADDR" }, + { 0x0f, "MISALIGNED_ADDR" }, + { 0x10, "INVALID_ADDR_SPACE" }, + { 0x11, "ILLEGAL_INSTR_PARAM2" }, + { 0x12, "INVALID_CONST_ADDR_LDC" }, + { 0x13, "GEOMETRY_SM_ERROR" }, + { 0x14, "DIVERGENT" }, + { 0x15, "WARP_EXIT" }, + {} +}; + +const struct nvkm_bitfield gf100_mp_global_error[] = { + { 0x00000001, "SM_TO_SM_FAULT" }, + { 0x00000002, "L1_ERROR" }, + { 0x00000004, "MULTIPLE_WARP_ERRORS" }, + { 0x00000008, "PHYSICAL_STACK_OVERFLOW" }, + { 0x00000010, "BPT_INT" }, + { 0x00000020, "BPT_PAUSE" }, + { 0x00000040, "SINGLE_STEP_COMPLETE" }, + { 0x20000000, "ECC_SEC_ERROR" }, + { 0x40000000, "ECC_DED_ERROR" }, + { 0x80000000, "TIMEOUT" }, + {} +}; + +void +gf100_gr_trap_mp(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 werr = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x648)); + u32 gerr = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x650)); + const struct nvkm_enum *warp; + char glob[128]; + + nvkm_snprintbf(glob, sizeof(glob), gf100_mp_global_error, gerr); + warp = nvkm_enum_find(gf100_mp_warp_error, werr & 0xffff); + + nvkm_error(subdev, "GPC%i/TPC%i/MP trap: " + "global %08x [%s] warp %04x [%s]\n", + gpc, tpc, gerr, glob, werr, warp ? warp->name : ""); + + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x648), 0x00000000); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x650), gerr); +} + +static void +gf100_gr_trap_tpc(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x0508)); + + if (stat & 0x00000001) { + u32 trap = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x0224)); + nvkm_error(subdev, "GPC%d/TPC%d/TEX: %08x\n", gpc, tpc, trap); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x0224), 0xc0000000); + stat &= ~0x00000001; + } + + if (stat & 0x00000002) { + gr->func->trap_mp(gr, gpc, tpc); + stat &= ~0x00000002; + } + + if (stat & 0x00000004) { + u32 trap = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x0084)); + nvkm_error(subdev, "GPC%d/TPC%d/POLY: %08x\n", gpc, tpc, trap); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x0084), 0xc0000000); + stat &= ~0x00000004; + } + + if (stat & 0x00000008) { + u32 trap = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x048c)); + nvkm_error(subdev, "GPC%d/TPC%d/L1C: %08x\n", gpc, tpc, trap); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x048c), 0xc0000000); + stat &= ~0x00000008; + } + + if (stat & 0x00000010) { + u32 trap = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x0430)); + nvkm_error(subdev, "GPC%d/TPC%d/MPC: %08x\n", gpc, tpc, trap); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x0430), 0xc0000000); + stat &= ~0x00000010; + } + + if (stat) { + nvkm_error(subdev, "GPC%d/TPC%d/%08x: unknown\n", gpc, tpc, stat); + } +} + +static void +gf100_gr_trap_gpc(struct gf100_gr *gr, int gpc) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, GPC_UNIT(gpc, 0x2c90)); + int tpc; + + if (stat & 0x00000001) { + gf100_gr_trap_gpc_rop(gr, gpc); + stat &= ~0x00000001; + } + + if (stat & 0x00000002) { + u32 trap = nvkm_rd32(device, GPC_UNIT(gpc, 0x0900)); + nvkm_error(subdev, "GPC%d/ZCULL: %08x\n", gpc, trap); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0900), 0xc0000000); + stat &= ~0x00000002; + } + + if (stat & 0x00000004) { + u32 trap = nvkm_rd32(device, GPC_UNIT(gpc, 0x1028)); + nvkm_error(subdev, "GPC%d/CCACHE: %08x\n", gpc, trap); + nvkm_wr32(device, GPC_UNIT(gpc, 0x1028), 0xc0000000); + stat &= ~0x00000004; + } + + if (stat & 0x00000008) { + u32 trap = nvkm_rd32(device, GPC_UNIT(gpc, 0x0824)); + nvkm_error(subdev, "GPC%d/ESETUP: %08x\n", gpc, trap); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0824), 0xc0000000); + stat &= ~0x00000009; + } + + for (tpc = 0; tpc < gr->tpc_nr[gpc]; tpc++) { + u32 mask = 0x00010000 << tpc; + if (stat & mask) { + gf100_gr_trap_tpc(gr, gpc, tpc); + nvkm_wr32(device, GPC_UNIT(gpc, 0x2c90), mask); + stat &= ~mask; + } + } + + if (stat) { + nvkm_error(subdev, "GPC%d/%08x: unknown\n", gpc, stat); + } +} + +static void +gf100_gr_trap_intr(struct gf100_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + char error[128]; + u32 trap = nvkm_rd32(device, 0x400108); + int rop, gpc; + + if (trap & 0x00000001) { + u32 stat = nvkm_rd32(device, 0x404000); + + nvkm_snprintbf(error, sizeof(error), gf100_dispatch_error, + stat & 0x3fffffff); + nvkm_error(subdev, "DISPATCH %08x [%s]\n", stat, error); + nvkm_wr32(device, 0x404000, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x00000001); + trap &= ~0x00000001; + } + + if (trap & 0x00000002) { + u32 stat = nvkm_rd32(device, 0x404600); + + nvkm_snprintbf(error, sizeof(error), gf100_m2mf_error, + stat & 0x3fffffff); + nvkm_error(subdev, "M2MF %08x [%s]\n", stat, error); + + nvkm_wr32(device, 0x404600, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x00000002); + trap &= ~0x00000002; + } + + if (trap & 0x00000008) { + u32 stat = nvkm_rd32(device, 0x408030); + + nvkm_snprintbf(error, sizeof(error), gf100_ccache_error, + stat & 0x3fffffff); + nvkm_error(subdev, "CCACHE %08x [%s]\n", stat, error); + nvkm_wr32(device, 0x408030, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x00000008); + trap &= ~0x00000008; + } + + if (trap & 0x00000010) { + u32 stat = nvkm_rd32(device, 0x405840); + nvkm_error(subdev, "SHADER %08x, sph: 0x%06x, stage: 0x%02x\n", + stat, stat & 0xffffff, (stat >> 24) & 0x3f); + nvkm_wr32(device, 0x405840, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x00000010); + trap &= ~0x00000010; + } + + if (trap & 0x00000040) { + u32 stat = nvkm_rd32(device, 0x40601c); + + nvkm_snprintbf(error, sizeof(error), gf100_unk6_error, + stat & 0x3fffffff); + nvkm_error(subdev, "UNK6 %08x [%s]\n", stat, error); + + nvkm_wr32(device, 0x40601c, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x00000040); + trap &= ~0x00000040; + } + + if (trap & 0x00000080) { + u32 stat = nvkm_rd32(device, 0x404490); + u32 pc = nvkm_rd32(device, 0x404494); + u32 op = nvkm_rd32(device, 0x40449c); + + nvkm_snprintbf(error, sizeof(error), gf100_macro_error, + stat & 0x1fffffff); + nvkm_error(subdev, "MACRO %08x [%s], pc: 0x%03x%s, op: 0x%08x\n", + stat, error, pc & 0x7ff, + (pc & 0x10000000) ? "" : " (invalid)", + op); + + nvkm_wr32(device, 0x404490, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x00000080); + trap &= ~0x00000080; + } + + if (trap & 0x00000100) { + u32 stat = nvkm_rd32(device, 0x407020) & 0x3fffffff; + + nvkm_snprintbf(error, sizeof(error), gk104_sked_error, stat); + nvkm_error(subdev, "SKED: %08x [%s]\n", stat, error); + + if (stat) + nvkm_wr32(device, 0x407020, 0x40000000); + nvkm_wr32(device, 0x400108, 0x00000100); + trap &= ~0x00000100; + } + + if (trap & 0x01000000) { + u32 stat = nvkm_rd32(device, 0x400118); + for (gpc = 0; stat && gpc < gr->gpc_nr; gpc++) { + u32 mask = 0x00000001 << gpc; + if (stat & mask) { + gf100_gr_trap_gpc(gr, gpc); + nvkm_wr32(device, 0x400118, mask); + stat &= ~mask; + } + } + nvkm_wr32(device, 0x400108, 0x01000000); + trap &= ~0x01000000; + } + + if (trap & 0x02000000) { + for (rop = 0; rop < gr->rop_nr; rop++) { + u32 statz = nvkm_rd32(device, ROP_UNIT(rop, 0x070)); + u32 statc = nvkm_rd32(device, ROP_UNIT(rop, 0x144)); + nvkm_error(subdev, "ROP%d %08x %08x\n", + rop, statz, statc); + nvkm_wr32(device, ROP_UNIT(rop, 0x070), 0xc0000000); + nvkm_wr32(device, ROP_UNIT(rop, 0x144), 0xc0000000); + } + nvkm_wr32(device, 0x400108, 0x02000000); + trap &= ~0x02000000; + } + + if (trap) { + nvkm_error(subdev, "TRAP UNHANDLED %08x\n", trap); + nvkm_wr32(device, 0x400108, trap); + } +} + +static void +gf100_gr_ctxctl_debug_unit(struct gf100_gr *gr, u32 base) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + nvkm_error(subdev, "%06x - done %08x\n", base, + nvkm_rd32(device, base + 0x400)); + nvkm_error(subdev, "%06x - stat %08x %08x %08x %08x\n", base, + nvkm_rd32(device, base + 0x800), + nvkm_rd32(device, base + 0x804), + nvkm_rd32(device, base + 0x808), + nvkm_rd32(device, base + 0x80c)); + nvkm_error(subdev, "%06x - stat %08x %08x %08x %08x\n", base, + nvkm_rd32(device, base + 0x810), + nvkm_rd32(device, base + 0x814), + nvkm_rd32(device, base + 0x818), + nvkm_rd32(device, base + 0x81c)); +} + +void +gf100_gr_ctxctl_debug(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 gpcnr = nvkm_rd32(device, 0x409604) & 0xffff; + u32 gpc; + + gf100_gr_ctxctl_debug_unit(gr, 0x409000); + for (gpc = 0; gpc < gpcnr; gpc++) + gf100_gr_ctxctl_debug_unit(gr, 0x502000 + (gpc * 0x8000)); +} + +static void +gf100_gr_ctxctl_isr(struct gf100_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x409c18); + + if (!gr->firmware && (stat & 0x00000001)) { + u32 code = nvkm_rd32(device, 0x409814); + if (code == E_BAD_FWMTHD) { + u32 class = nvkm_rd32(device, 0x409808); + u32 addr = nvkm_rd32(device, 0x40980c); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00003ffc); + u32 data = nvkm_rd32(device, 0x409810); + + nvkm_error(subdev, "FECS MTHD subc %d class %04x " + "mthd %04x data %08x\n", + subc, class, mthd, data); + } else { + nvkm_error(subdev, "FECS ucode error %d\n", code); + } + nvkm_wr32(device, 0x409c20, 0x00000001); + stat &= ~0x00000001; + } + + if (!gr->firmware && (stat & 0x00080000)) { + nvkm_error(subdev, "FECS watchdog timeout\n"); + gf100_gr_ctxctl_debug(gr); + nvkm_wr32(device, 0x409c20, 0x00080000); + stat &= ~0x00080000; + } + + if (stat) { + nvkm_error(subdev, "FECS %08x\n", stat); + gf100_gr_ctxctl_debug(gr); + nvkm_wr32(device, 0x409c20, stat); + } +} + +static void +gf100_gr_intr(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fifo_chan *chan; + unsigned long flags; + u64 inst = nvkm_rd32(device, 0x409b00) & 0x0fffffff; + u32 stat = nvkm_rd32(device, 0x400100); + u32 addr = nvkm_rd32(device, 0x400704); + u32 mthd = (addr & 0x00003ffc); + u32 subc = (addr & 0x00070000) >> 16; + u32 data = nvkm_rd32(device, 0x400708); + u32 code = nvkm_rd32(device, 0x400110); + u32 class; + const char *name = "unknown"; + int chid = -1; + + chan = nvkm_fifo_chan_inst(device->fifo, (u64)inst << 12, &flags); + if (chan) { + name = chan->object.client->name; + chid = chan->chid; + } + + if (device->card_type < NV_E0 || subc < 4) + class = nvkm_rd32(device, 0x404200 + (subc * 4)); + else + class = 0x0000; + + if (stat & 0x00000001) { + /* + * notifier interrupt, only needed for cyclestats + * can be safely ignored + */ + nvkm_wr32(device, 0x400100, 0x00000001); + stat &= ~0x00000001; + } + + if (stat & 0x00000010) { + if (!gf100_gr_mthd_sw(device, class, mthd, data)) { + nvkm_error(subdev, "ILLEGAL_MTHD ch %d [%010llx %s] " + "subc %d class %04x mthd %04x data %08x\n", + chid, inst << 12, name, subc, + class, mthd, data); + } + nvkm_wr32(device, 0x400100, 0x00000010); + stat &= ~0x00000010; + } + + if (stat & 0x00000020) { + nvkm_error(subdev, "ILLEGAL_CLASS ch %d [%010llx %s] " + "subc %d class %04x mthd %04x data %08x\n", + chid, inst << 12, name, subc, class, mthd, data); + nvkm_wr32(device, 0x400100, 0x00000020); + stat &= ~0x00000020; + } + + if (stat & 0x00100000) { + const struct nvkm_enum *en = + nvkm_enum_find(nv50_data_error_names, code); + nvkm_error(subdev, "DATA_ERROR %08x [%s] ch %d [%010llx %s] " + "subc %d class %04x mthd %04x data %08x\n", + code, en ? en->name : "", chid, inst << 12, + name, subc, class, mthd, data); + nvkm_wr32(device, 0x400100, 0x00100000); + stat &= ~0x00100000; + } + + if (stat & 0x00200000) { + nvkm_error(subdev, "TRAP ch %d [%010llx %s]\n", + chid, inst << 12, name); + gf100_gr_trap_intr(gr); + nvkm_wr32(device, 0x400100, 0x00200000); + stat &= ~0x00200000; + } + + if (stat & 0x00080000) { + gf100_gr_ctxctl_isr(gr); + nvkm_wr32(device, 0x400100, 0x00080000); + stat &= ~0x00080000; + } + + if (stat) { + nvkm_error(subdev, "intr %08x\n", stat); + nvkm_wr32(device, 0x400100, stat); + } + + nvkm_wr32(device, 0x400500, 0x00010001); + nvkm_fifo_chan_put(device->fifo, flags, &chan); +} + +static void +gf100_gr_init_fw(struct nvkm_falcon *falcon, + struct nvkm_blob *code, struct nvkm_blob *data) +{ + nvkm_falcon_load_dmem(falcon, data->data, 0x0, data->size, 0); + nvkm_falcon_load_imem(falcon, code->data, 0x0, code->size, 0, 0, false); +} + +static void +gf100_gr_init_csdata(struct gf100_gr *gr, + const struct gf100_gr_pack *pack, + u32 falcon, u32 starstar, u32 base) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const struct gf100_gr_pack *iter; + const struct gf100_gr_init *init; + u32 addr = ~0, prev = ~0, xfer = 0; + u32 star, temp; + + nvkm_wr32(device, falcon + 0x01c0, 0x02000000 + starstar); + star = nvkm_rd32(device, falcon + 0x01c4); + temp = nvkm_rd32(device, falcon + 0x01c4); + if (temp > star) + star = temp; + nvkm_wr32(device, falcon + 0x01c0, 0x01000000 + star); + + pack_for_each_init(init, iter, pack) { + u32 head = init->addr - base; + u32 tail = head + init->count * init->pitch; + while (head < tail) { + if (head != prev + 4 || xfer >= 32) { + if (xfer) { + u32 data = ((--xfer << 26) | addr); + nvkm_wr32(device, falcon + 0x01c4, data); + star += 4; + } + addr = head; + xfer = 0; + } + prev = head; + xfer = xfer + 1; + head = head + init->pitch; + } + } + + nvkm_wr32(device, falcon + 0x01c4, (--xfer << 26) | addr); + nvkm_wr32(device, falcon + 0x01c0, 0x01000004 + starstar); + nvkm_wr32(device, falcon + 0x01c4, star + 4); +} + +/* Initialize context from an external (secure or not) firmware */ +static int +gf100_gr_init_ctxctl_ext(struct gf100_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 lsf_mask = 0; + int ret; + + /* load fuc microcode */ + nvkm_mc_unk260(device, 0); + + /* securely-managed falcons must be reset using secure boot */ + + if (!nvkm_acr_managed_falcon(device, NVKM_ACR_LSF_FECS)) { + gf100_gr_init_fw(&gr->fecs.falcon, &gr->fecs.inst, + &gr->fecs.data); + } else { + lsf_mask |= BIT(NVKM_ACR_LSF_FECS); + } + + if (!nvkm_acr_managed_falcon(device, NVKM_ACR_LSF_GPCCS)) { + gf100_gr_init_fw(&gr->gpccs.falcon, &gr->gpccs.inst, + &gr->gpccs.data); + } else { + lsf_mask |= BIT(NVKM_ACR_LSF_GPCCS); + } + + if (lsf_mask) { + ret = nvkm_acr_bootstrap_falcons(device, lsf_mask); + if (ret) + return ret; + } + + nvkm_mc_unk260(device, 1); + + /* start both of them running */ + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x41a10c, 0x00000000); + nvkm_wr32(device, 0x40910c, 0x00000000); + + nvkm_falcon_start(&gr->gpccs.falcon); + nvkm_falcon_start(&gr->fecs.falcon); + + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800) & 0x00000001) + break; + ) < 0) + return -EBUSY; + + gf100_gr_fecs_set_watchdog_timeout(gr, 0x7fffffff); + + /* Determine how much memory is required to store main context image. */ + ret = gf100_gr_fecs_discover_image_size(gr, &gr->size); + if (ret) + return ret; + + /* Determine how much memory is required to store ZCULL image. */ + ret = gf100_gr_fecs_discover_zcull_image_size(gr, &gr->size_zcull); + if (ret) + return ret; + + /* Determine how much memory is required to store PerfMon image. */ + ret = gf100_gr_fecs_discover_pm_image_size(gr, &gr->size_pm); + if (ret) + return ret; + + /*XXX: We (likely) require PMU support to even bother with this. + * + * Also, it seems like not all GPUs support ELPG. Traces I + * have here show RM enabling it on Kepler/Turing, but none + * of the GPUs between those. NVGPU decides this by PCIID. + */ + if (0) { + ret = gf100_gr_fecs_elpg_bind(gr); + if (ret) + return ret; + } + + /* Generate golden context image. */ + if (gr->data == NULL) { + int ret = gf100_grctx_generate(gr); + if (ret) { + nvkm_error(subdev, "failed to construct context\n"); + return ret; + } + } + + return 0; +} + +static int +gf100_gr_init_ctxctl_int(struct gf100_gr *gr) +{ + const struct gf100_grctx_func *grctx = gr->func->grctx; + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + + if (!gr->func->fecs.ucode) { + return -ENOSYS; + } + + /* load HUB microcode */ + nvkm_mc_unk260(device, 0); + nvkm_falcon_load_dmem(&gr->fecs.falcon, + gr->func->fecs.ucode->data.data, 0x0, + gr->func->fecs.ucode->data.size, 0); + nvkm_falcon_load_imem(&gr->fecs.falcon, + gr->func->fecs.ucode->code.data, 0x0, + gr->func->fecs.ucode->code.size, 0, 0, false); + + /* load GPC microcode */ + nvkm_falcon_load_dmem(&gr->gpccs.falcon, + gr->func->gpccs.ucode->data.data, 0x0, + gr->func->gpccs.ucode->data.size, 0); + nvkm_falcon_load_imem(&gr->gpccs.falcon, + gr->func->gpccs.ucode->code.data, 0x0, + gr->func->gpccs.ucode->code.size, 0, 0, false); + nvkm_mc_unk260(device, 1); + + /* load register lists */ + gf100_gr_init_csdata(gr, grctx->hub, 0x409000, 0x000, 0x000000); + gf100_gr_init_csdata(gr, grctx->gpc_0, 0x41a000, 0x000, 0x418000); + gf100_gr_init_csdata(gr, grctx->gpc_1, 0x41a000, 0x000, 0x418000); + gf100_gr_init_csdata(gr, grctx->tpc, 0x41a000, 0x004, 0x419800); + gf100_gr_init_csdata(gr, grctx->ppc, 0x41a000, 0x008, 0x41be00); + + /* start HUB ucode running, it'll init the GPCs */ + nvkm_wr32(device, 0x40910c, 0x00000000); + nvkm_wr32(device, 0x409100, 0x00000002); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800) & 0x80000000) + break; + ) < 0) { + gf100_gr_ctxctl_debug(gr); + return -EBUSY; + } + + gr->size = nvkm_rd32(device, 0x409804); + if (gr->data == NULL) { + int ret = gf100_grctx_generate(gr); + if (ret) { + nvkm_error(subdev, "failed to construct context\n"); + return ret; + } + } + + return 0; +} + +int +gf100_gr_init_ctxctl(struct gf100_gr *gr) +{ + int ret; + + if (gr->firmware) + ret = gf100_gr_init_ctxctl_ext(gr); + else + ret = gf100_gr_init_ctxctl_int(gr); + + return ret; +} + +void +gf100_gr_oneinit_sm_id(struct gf100_gr *gr) +{ + int tpc, gpc; + for (tpc = 0; tpc < gr->tpc_max; tpc++) { + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + if (tpc < gr->tpc_nr[gpc]) { + gr->sm[gr->sm_nr].gpc = gpc; + gr->sm[gr->sm_nr].tpc = tpc; + gr->sm_nr++; + } + } + } +} + +void +gf100_gr_oneinit_tiles(struct gf100_gr *gr) +{ + static const u8 primes[] = { + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61 + }; + int init_frac[GPC_MAX], init_err[GPC_MAX], run_err[GPC_MAX], i, j; + u32 mul_factor, comm_denom; + u8 gpc_map[GPC_MAX]; + bool sorted; + + switch (gr->tpc_total) { + case 15: gr->screen_tile_row_offset = 0x06; break; + case 14: gr->screen_tile_row_offset = 0x05; break; + case 13: gr->screen_tile_row_offset = 0x02; break; + case 11: gr->screen_tile_row_offset = 0x07; break; + case 10: gr->screen_tile_row_offset = 0x06; break; + case 7: + case 5: gr->screen_tile_row_offset = 0x01; break; + case 3: gr->screen_tile_row_offset = 0x02; break; + case 2: + case 1: gr->screen_tile_row_offset = 0x01; break; + default: gr->screen_tile_row_offset = 0x03; + for (i = 0; i < ARRAY_SIZE(primes); i++) { + if (gr->tpc_total % primes[i]) { + gr->screen_tile_row_offset = primes[i]; + break; + } + } + break; + } + + /* Sort GPCs by TPC count, highest-to-lowest. */ + for (i = 0; i < gr->gpc_nr; i++) + gpc_map[i] = i; + sorted = false; + + while (!sorted) { + for (sorted = true, i = 0; i < gr->gpc_nr - 1; i++) { + if (gr->tpc_nr[gpc_map[i + 1]] > + gr->tpc_nr[gpc_map[i + 0]]) { + u8 swap = gpc_map[i]; + gpc_map[i + 0] = gpc_map[i + 1]; + gpc_map[i + 1] = swap; + sorted = false; + } + } + } + + /* Determine tile->GPC mapping */ + mul_factor = gr->gpc_nr * gr->tpc_max; + if (mul_factor & 1) + mul_factor = 2; + else + mul_factor = 1; + + comm_denom = gr->gpc_nr * gr->tpc_max * mul_factor; + + for (i = 0; i < gr->gpc_nr; i++) { + init_frac[i] = gr->tpc_nr[gpc_map[i]] * gr->gpc_nr * mul_factor; + init_err[i] = i * gr->tpc_max * mul_factor - comm_denom/2; + run_err[i] = init_frac[i] + init_err[i]; + } + + for (i = 0; i < gr->tpc_total;) { + for (j = 0; j < gr->gpc_nr; j++) { + if ((run_err[j] * 2) >= comm_denom) { + gr->tile[i++] = gpc_map[j]; + run_err[j] += init_frac[j] - comm_denom; + } else { + run_err[j] += init_frac[j]; + } + } + } +} + +static int +gf100_gr_oneinit(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + int i, j; + + nvkm_pmu_pgob(device->pmu, false); + + gr->rop_nr = gr->func->rops(gr); + gr->gpc_nr = nvkm_rd32(device, 0x409604) & 0x0000001f; + for (i = 0; i < gr->gpc_nr; i++) { + gr->tpc_nr[i] = nvkm_rd32(device, GPC_UNIT(i, 0x2608)); + gr->tpc_max = max(gr->tpc_max, gr->tpc_nr[i]); + gr->tpc_total += gr->tpc_nr[i]; + gr->ppc_nr[i] = gr->func->ppc_nr; + for (j = 0; j < gr->ppc_nr[i]; j++) { + gr->ppc_tpc_mask[i][j] = + nvkm_rd32(device, GPC_UNIT(i, 0x0c30 + (j * 4))); + if (gr->ppc_tpc_mask[i][j] == 0) + continue; + gr->ppc_mask[i] |= (1 << j); + gr->ppc_tpc_nr[i][j] = hweight8(gr->ppc_tpc_mask[i][j]); + if (gr->ppc_tpc_min == 0 || + gr->ppc_tpc_min > gr->ppc_tpc_nr[i][j]) + gr->ppc_tpc_min = gr->ppc_tpc_nr[i][j]; + if (gr->ppc_tpc_max < gr->ppc_tpc_nr[i][j]) + gr->ppc_tpc_max = gr->ppc_tpc_nr[i][j]; + } + } + + memset(gr->tile, 0xff, sizeof(gr->tile)); + gr->func->oneinit_tiles(gr); + gr->func->oneinit_sm_id(gr); + return 0; +} + +static int +gf100_gr_init_(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + struct nvkm_subdev *subdev = &base->engine.subdev; + struct nvkm_device *device = subdev->device; + bool reset = device->chipset == 0x137 || device->chipset == 0x138; + u32 ret; + + /* On certain GP107/GP108 boards, we trigger a weird issue where + * GR will stop responding to PRI accesses after we've asked the + * SEC2 RTOS to boot the GR falcons. This happens with far more + * frequency when cold-booting a board (ie. returning from D3). + * + * The root cause for this is not known and has proven difficult + * to isolate, with many avenues being dead-ends. + * + * A workaround was discovered by Karol, whereby putting GR into + * reset for an extended period right before initialisation + * prevents the problem from occuring. + * + * XXX: As RM does not require any such workaround, this is more + * of a hack than a true fix. + */ + reset = nvkm_boolopt(device->cfgopt, "NvGrResetWar", reset); + if (reset) { + nvkm_mask(device, 0x000200, 0x00001000, 0x00000000); + nvkm_rd32(device, 0x000200); + msleep(50); + nvkm_mask(device, 0x000200, 0x00001000, 0x00001000); + nvkm_rd32(device, 0x000200); + } + + nvkm_pmu_pgob(gr->base.engine.subdev.device->pmu, false); + + ret = nvkm_falcon_get(&gr->fecs.falcon, subdev); + if (ret) + return ret; + + ret = nvkm_falcon_get(&gr->gpccs.falcon, subdev); + if (ret) + return ret; + + return gr->func->init(gr); +} + +static int +gf100_gr_fini(struct nvkm_gr *base, bool suspend) +{ + struct gf100_gr *gr = gf100_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + nvkm_falcon_put(&gr->gpccs.falcon, subdev); + nvkm_falcon_put(&gr->fecs.falcon, subdev); + return 0; +} + +static void * +gf100_gr_dtor(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + + kfree(gr->data); + + nvkm_falcon_dtor(&gr->gpccs.falcon); + nvkm_falcon_dtor(&gr->fecs.falcon); + + nvkm_blob_dtor(&gr->fecs.inst); + nvkm_blob_dtor(&gr->fecs.data); + nvkm_blob_dtor(&gr->gpccs.inst); + nvkm_blob_dtor(&gr->gpccs.data); + + vfree(gr->bundle); + vfree(gr->method); + vfree(gr->sw_ctx); + vfree(gr->sw_nonctx); + + return gr; +} + +static const struct nvkm_gr_func +gf100_gr_ = { + .dtor = gf100_gr_dtor, + .oneinit = gf100_gr_oneinit, + .init = gf100_gr_init_, + .fini = gf100_gr_fini, + .intr = gf100_gr_intr, + .units = gf100_gr_units, + .chan_new = gf100_gr_chan_new, + .object_get = gf100_gr_object_get, + .chsw_load = gf100_gr_chsw_load, + .ctxsw.pause = gf100_gr_fecs_stop_ctxsw, + .ctxsw.resume = gf100_gr_fecs_start_ctxsw, + .ctxsw.inst = gf100_gr_ctxsw_inst, +}; + +static const struct nvkm_falcon_func +gf100_gr_flcn = { + .fbif = 0x600, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .bind_context = nvkm_falcon_v1_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = nvkm_falcon_v1_enable, + .disable = nvkm_falcon_v1_disable, +}; + +int +gf100_gr_new_(const struct gf100_gr_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + struct gf100_gr *gr; + int ret; + + if (!(gr = kzalloc(sizeof(*gr), GFP_KERNEL))) + return -ENOMEM; + *pgr = &gr->base; + + ret = nvkm_gr_ctor(&gf100_gr_, device, type, inst, true, &gr->base); + if (ret) + return ret; + + fwif = nvkm_firmware_load(&gr->base.engine.subdev, fwif, "Gr", gr); + if (IS_ERR(fwif)) + return PTR_ERR(fwif); + + gr->func = fwif->func; + + ret = nvkm_falcon_ctor(&gf100_gr_flcn, &gr->base.engine.subdev, + "fecs", 0x409000, &gr->fecs.falcon); + if (ret) + return ret; + + mutex_init(&gr->fecs.mutex); + + ret = nvkm_falcon_ctor(&gf100_gr_flcn, &gr->base.engine.subdev, + "gpccs", 0x41a000, &gr->gpccs.falcon); + if (ret) + return ret; + + return 0; +} + +void +gf100_gr_init_num_tpc_per_gpc(struct gf100_gr *gr, bool pd, bool ds) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int gpc, i, j; + u32 data; + + for (gpc = 0, i = 0; i < 4; i++) { + for (data = 0, j = 0; j < 8 && gpc < gr->gpc_nr; j++, gpc++) + data |= gr->tpc_nr[gpc] << (j * 4); + if (pd) + nvkm_wr32(device, 0x406028 + (i * 4), data); + if (ds) + nvkm_wr32(device, 0x405870 + (i * 4), data); + } +} + +void +gf100_gr_init_400054(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x400054, 0x34ce3464); +} + +void +gf100_gr_init_shader_exceptions(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); +} + +void +gf100_gr_init_tex_hww_esr(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); +} + +void +gf100_gr_init_419eb4(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419eb4, 0x00001000, 0x00001000); +} + +void +gf100_gr_init_419cc0(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int gpc, tpc; + + nvkm_mask(device, 0x419cc0, 0x00000008, 0x00000008); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (tpc = 0; tpc < gr->tpc_nr[gpc]; tpc++) + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + } +} + +void +gf100_gr_init_40601c(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x40601c, 0xc0000000); +} + +void +gf100_gr_init_fecs_exceptions(struct gf100_gr *gr) +{ + const u32 data = gr->firmware ? 0x000e0000 : 0x000e0001; + nvkm_wr32(gr->base.engine.subdev.device, 0x409c24, data); +} + +void +gf100_gr_init_gpc_mmu(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nvkm_fb *fb = device->fb; + + nvkm_wr32(device, 0x418880, nvkm_rd32(device, 0x100c80) & 0x00000001); + nvkm_wr32(device, 0x4188a4, 0x03000000); + nvkm_wr32(device, 0x418888, 0x00000000); + nvkm_wr32(device, 0x41888c, 0x00000000); + nvkm_wr32(device, 0x418890, 0x00000000); + nvkm_wr32(device, 0x418894, 0x00000000); + nvkm_wr32(device, 0x4188b4, nvkm_memory_addr(fb->mmu_wr) >> 8); + nvkm_wr32(device, 0x4188b8, nvkm_memory_addr(fb->mmu_rd) >> 8); +} + +void +gf100_gr_init_num_active_ltcs(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, GPC_BCAST(0x08ac), nvkm_rd32(device, 0x100800)); +} + +void +gf100_gr_init_zcull(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, gr->tpc_total); + const u8 tile_nr = ALIGN(gr->tpc_total, 32); + u8 bank[GPC_MAX] = {}, gpc, i, j; + u32 data; + + for (i = 0; i < tile_nr; i += 8) { + for (data = 0, j = 0; j < 8 && i + j < gr->tpc_total; j++) { + data |= bank[gr->tile[i + j]] << (j * 4); + bank[gr->tile[i + j]]++; + } + nvkm_wr32(device, GPC_BCAST(0x0980 + ((i / 8) * 4)), data); + } + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + nvkm_wr32(device, GPC_UNIT(gpc, 0x0914), + gr->screen_tile_row_offset << 8 | gr->tpc_nr[gpc]); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0910), 0x00040000 | + gr->tpc_total); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nvkm_wr32(device, GPC_BCAST(0x1bd4), magicgpc918); +} + +void +gf100_gr_init_vsc_stream_master(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, TPC_UNIT(0, 0, 0x05c), 0x00000001, 0x00000001); +} + +int +gf100_gr_init(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int gpc, tpc, rop; + + if (gr->func->init_419bd8) + gr->func->init_419bd8(gr); + + gr->func->init_gpc_mmu(gr); + + if (gr->sw_nonctx) + gf100_gr_mmio(gr, gr->sw_nonctx); + else + gf100_gr_mmio(gr, gr->func->mmio); + + gf100_gr_wait_idle(gr); + + if (gr->func->init_r405a14) + gr->func->init_r405a14(gr); + + if (gr->func->clkgate_pack) + nvkm_therm_clkgate_init(device->therm, gr->func->clkgate_pack); + + if (gr->func->init_bios) + gr->func->init_bios(gr); + + gr->func->init_vsc_stream_master(gr); + gr->func->init_zcull(gr); + gr->func->init_num_active_ltcs(gr); + if (gr->func->init_rop_active_fbps) + gr->func->init_rop_active_fbps(gr); + if (gr->func->init_bios_2) + gr->func->init_bios_2(gr); + if (gr->func->init_swdx_pes_mask) + gr->func->init_swdx_pes_mask(gr); + if (gr->func->init_fs) + gr->func->init_fs(gr); + + nvkm_wr32(device, 0x400500, 0x00010001); + + nvkm_wr32(device, 0x400100, 0xffffffff); + nvkm_wr32(device, 0x40013c, 0xffffffff); + nvkm_wr32(device, 0x400124, 0x00000002); + + gr->func->init_fecs_exceptions(gr); + if (gr->func->init_ds_hww_esr_2) + gr->func->init_ds_hww_esr_2(gr); + + nvkm_wr32(device, 0x404000, 0xc0000000); + nvkm_wr32(device, 0x404600, 0xc0000000); + nvkm_wr32(device, 0x408030, 0xc0000000); + + if (gr->func->init_40601c) + gr->func->init_40601c(gr); + + nvkm_wr32(device, 0x406018, 0xc0000000); + nvkm_wr32(device, 0x404490, 0xc0000000); + + if (gr->func->init_sked_hww_esr) + gr->func->init_sked_hww_esr(gr); + + nvkm_wr32(device, 0x405840, 0xc0000000); + nvkm_wr32(device, 0x405844, 0x00ffffff); + + if (gr->func->init_419cc0) + gr->func->init_419cc0(gr); + if (gr->func->init_419eb4) + gr->func->init_419eb4(gr); + if (gr->func->init_419c9c) + gr->func->init_419c9c(gr); + + if (gr->func->init_ppc_exceptions) + gr->func->init_ppc_exceptions(gr); + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + nvkm_wr32(device, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nvkm_wr32(device, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < gr->tpc_nr[gpc]; tpc++) { + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + if (gr->func->init_tex_hww_esr) + gr->func->init_tex_hww_esr(gr, gpc, tpc); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + if (gr->func->init_504430) + gr->func->init_504430(gr, gpc, tpc); + gr->func->init_shader_exceptions(gr, gpc, tpc); + } + nvkm_wr32(device, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nvkm_wr32(device, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } + + for (rop = 0; rop < gr->rop_nr; rop++) { + nvkm_wr32(device, ROP_UNIT(rop, 0x144), 0x40000000); + nvkm_wr32(device, ROP_UNIT(rop, 0x070), 0x40000000); + nvkm_wr32(device, ROP_UNIT(rop, 0x204), 0xffffffff); + nvkm_wr32(device, ROP_UNIT(rop, 0x208), 0xffffffff); + } + + nvkm_wr32(device, 0x400108, 0xffffffff); + nvkm_wr32(device, 0x400138, 0xffffffff); + nvkm_wr32(device, 0x400118, 0xffffffff); + nvkm_wr32(device, 0x400130, 0xffffffff); + nvkm_wr32(device, 0x40011c, 0xffffffff); + nvkm_wr32(device, 0x400134, 0xffffffff); + + if (gr->func->init_400054) + gr->func->init_400054(gr); + + gf100_gr_zbc_init(gr); + + if (gr->func->init_4188a4) + gr->func->init_4188a4(gr); + + return gf100_gr_init_ctxctl(gr); +} + +#include "fuc/hubgf100.fuc3.h" + +struct gf100_gr_ucode +gf100_gr_fecs_ucode = { + .code.data = gf100_grhub_code, + .code.size = sizeof(gf100_grhub_code), + .data.data = gf100_grhub_data, + .data.size = sizeof(gf100_grhub_data), +}; + +#include "fuc/gpcgf100.fuc3.h" + +struct gf100_gr_ucode +gf100_gr_gpccs_ucode = { + .code.data = gf100_grgpc_code, + .code.size = sizeof(gf100_grgpc_code), + .data.data = gf100_grgpc_data, + .data.size = sizeof(gf100_grgpc_data), +}; + +static const struct gf100_gr_func +gf100_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gf100_gr_init_vsc_stream_master, + .init_zcull = gf100_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_40601c = gf100_gr_init_40601c, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gf100_gr_init_419eb4, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gf100_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .grctx = &gf100_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, FERMI_MEMORY_TO_MEMORY_FORMAT_A }, + { -1, -1, FERMI_A, &gf100_fermi }, + { -1, -1, FERMI_COMPUTE_A }, + {} + } +}; + +int +gf100_gr_nofw(struct gf100_gr *gr, int ver, const struct gf100_gr_fwif *fwif) +{ + gr->firmware = false; + return 0; +} + +static int +gf100_gr_load_fw(struct gf100_gr *gr, const char *name, + struct nvkm_blob *blob) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const struct firmware *fw; + char f[32]; + int ret; + + snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, name); + ret = request_firmware(&fw, f, device->dev); + if (ret) { + snprintf(f, sizeof(f), "nouveau/%s", name); + ret = request_firmware(&fw, f, device->dev); + if (ret) { + nvkm_error(subdev, "failed to load %s\n", name); + return ret; + } + } + + blob->size = fw->size; + blob->data = kmemdup(fw->data, blob->size, GFP_KERNEL); + release_firmware(fw); + return (blob->data != NULL) ? 0 : -ENOMEM; +} + +int +gf100_gr_load(struct gf100_gr *gr, int ver, const struct gf100_gr_fwif *fwif) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + if (!nvkm_boolopt(device->cfgopt, "NvGrUseFW", false)) + return -EINVAL; + + if (gf100_gr_load_fw(gr, "fuc409c", &gr->fecs.inst) || + gf100_gr_load_fw(gr, "fuc409d", &gr->fecs.data) || + gf100_gr_load_fw(gr, "fuc41ac", &gr->gpccs.inst) || + gf100_gr_load_fw(gr, "fuc41ad", &gr->gpccs.data)) + return -ENOENT; + + gr->firmware = true; + return 0; +} + +static const struct gf100_gr_fwif +gf100_gr_fwif[] = { + { -1, gf100_gr_load, &gf100_gr }, + { -1, gf100_gr_nofw, &gf100_gr }, + {} +}; + +int +gf100_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gf100_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h new file mode 100644 index 000000000..c0038f906 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h @@ -0,0 +1,421 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#ifndef __GF100_GR_H__ +#define __GF100_GR_H__ +#define gf100_gr(p) container_of((p), struct gf100_gr, base) +#include "priv.h" + +#include +#include +#include +#include + +struct nvkm_acr_lsfw; + +#define GPC_MAX 32 +#define TPC_MAX_PER_GPC 8 +#define TPC_MAX (GPC_MAX * TPC_MAX_PER_GPC) + +#define ROP_BCAST(r) (0x408800 + (r)) +#define ROP_UNIT(u, r) (0x410000 + (u) * 0x400 + (r)) +#define GPC_BCAST(r) (0x418000 + (r)) +#define GPC_UNIT(t, r) (0x500000 + (t) * 0x8000 + (r)) +#define PPC_UNIT(t, m, r) (0x503000 + (t) * 0x8000 + (m) * 0x200 + (r)) +#define TPC_UNIT(t, m, r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r)) + +struct gf100_gr_data { + u32 size; + u32 align; + bool priv; +}; + +struct gf100_gr_mmio { + u32 addr; + u32 data; + u32 shift; + int buffer; +}; + +struct gf100_gr_zbc_color { + u32 format; + u32 ds[4]; + u32 l2[4]; +}; + +struct gf100_gr_zbc_depth { + u32 format; + u32 ds; + u32 l2; +}; + +struct gf100_gr_zbc_stencil { + u32 format; + u32 ds; + u32 l2; +}; + +struct gf100_gr { + const struct gf100_gr_func *func; + struct nvkm_gr base; + + struct { + struct nvkm_falcon falcon; + struct nvkm_blob inst; + struct nvkm_blob data; + + struct mutex mutex; + u32 disable; + } fecs; + + struct { + struct nvkm_falcon falcon; + struct nvkm_blob inst; + struct nvkm_blob data; + } gpccs; + + bool firmware; + + /* + * Used if the register packs are loaded from NVIDIA fw instead of + * using hardcoded arrays. To be allocated with vzalloc(). + */ + struct gf100_gr_pack *sw_nonctx; + struct gf100_gr_pack *sw_ctx; + struct gf100_gr_pack *bundle; + struct gf100_gr_pack *method; + + struct gf100_gr_zbc_color zbc_color[NVKM_LTC_MAX_ZBC_CNT]; + struct gf100_gr_zbc_depth zbc_depth[NVKM_LTC_MAX_ZBC_CNT]; + struct gf100_gr_zbc_stencil zbc_stencil[NVKM_LTC_MAX_ZBC_CNT]; + + u8 rop_nr; + u8 gpc_nr; + u8 tpc_nr[GPC_MAX]; + u8 tpc_max; + u8 tpc_total; + u8 ppc_nr[GPC_MAX]; + u8 ppc_mask[GPC_MAX]; + u8 ppc_tpc_mask[GPC_MAX][4]; + u8 ppc_tpc_nr[GPC_MAX][4]; + u8 ppc_tpc_min; + u8 ppc_tpc_max; + + u8 screen_tile_row_offset; + u8 tile[TPC_MAX]; + + struct { + u8 gpc; + u8 tpc; + } sm[TPC_MAX]; + u8 sm_nr; + + struct gf100_gr_data mmio_data[4]; + struct gf100_gr_mmio mmio_list[4096/8]; + u32 size; + u32 *data; + u32 size_zcull; + u32 size_pm; +}; + +int gf100_gr_fecs_bind_pointer(struct gf100_gr *, u32 inst); + +struct gf100_gr_func_zbc { + void (*clear_color)(struct gf100_gr *, int zbc); + void (*clear_depth)(struct gf100_gr *, int zbc); + int (*stencil_get)(struct gf100_gr *, int format, + const u32 ds, const u32 l2); + void (*clear_stencil)(struct gf100_gr *, int zbc); +}; + +struct gf100_gr_func { + void (*oneinit_tiles)(struct gf100_gr *); + void (*oneinit_sm_id)(struct gf100_gr *); + int (*init)(struct gf100_gr *); + void (*init_419bd8)(struct gf100_gr *); + void (*init_gpc_mmu)(struct gf100_gr *); + void (*init_r405a14)(struct gf100_gr *); + void (*init_bios)(struct gf100_gr *); + void (*init_vsc_stream_master)(struct gf100_gr *); + void (*init_zcull)(struct gf100_gr *); + void (*init_num_active_ltcs)(struct gf100_gr *); + void (*init_rop_active_fbps)(struct gf100_gr *); + void (*init_bios_2)(struct gf100_gr *); + void (*init_swdx_pes_mask)(struct gf100_gr *); + void (*init_fs)(struct gf100_gr *); + void (*init_fecs_exceptions)(struct gf100_gr *); + void (*init_ds_hww_esr_2)(struct gf100_gr *); + void (*init_40601c)(struct gf100_gr *); + void (*init_sked_hww_esr)(struct gf100_gr *); + void (*init_419cc0)(struct gf100_gr *); + void (*init_419eb4)(struct gf100_gr *); + void (*init_419c9c)(struct gf100_gr *); + void (*init_ppc_exceptions)(struct gf100_gr *); + void (*init_tex_hww_esr)(struct gf100_gr *, int gpc, int tpc); + void (*init_504430)(struct gf100_gr *, int gpc, int tpc); + void (*init_shader_exceptions)(struct gf100_gr *, int gpc, int tpc); + void (*init_400054)(struct gf100_gr *); + void (*init_4188a4)(struct gf100_gr *); + void (*trap_mp)(struct gf100_gr *, int gpc, int tpc); + void (*set_hww_esr_report_mask)(struct gf100_gr *); + const struct gf100_gr_pack *mmio; + struct { + struct gf100_gr_ucode *ucode; + } fecs; + struct { + struct gf100_gr_ucode *ucode; + } gpccs; + int (*rops)(struct gf100_gr *); + int gpc_nr; + int tpc_nr; + int ppc_nr; + const struct gf100_grctx_func *grctx; + const struct nvkm_therm_clkgate_pack *clkgate_pack; + const struct gf100_gr_func_zbc *zbc; + struct nvkm_sclass sclass[]; +}; + +int gf100_gr_rops(struct gf100_gr *); +void gf100_gr_oneinit_tiles(struct gf100_gr *); +void gf100_gr_oneinit_sm_id(struct gf100_gr *); +int gf100_gr_init(struct gf100_gr *); +void gf100_gr_init_vsc_stream_master(struct gf100_gr *); +void gf100_gr_init_zcull(struct gf100_gr *); +void gf100_gr_init_num_active_ltcs(struct gf100_gr *); +void gf100_gr_init_fecs_exceptions(struct gf100_gr *); +void gf100_gr_init_40601c(struct gf100_gr *); +void gf100_gr_init_419cc0(struct gf100_gr *); +void gf100_gr_init_419eb4(struct gf100_gr *); +void gf100_gr_init_tex_hww_esr(struct gf100_gr *, int, int); +void gf100_gr_init_shader_exceptions(struct gf100_gr *, int, int); +void gf100_gr_init_400054(struct gf100_gr *); +void gf100_gr_init_num_tpc_per_gpc(struct gf100_gr *, bool, bool); +extern const struct gf100_gr_func_zbc gf100_gr_zbc; + +void gf117_gr_init_zcull(struct gf100_gr *); + +void gk104_gr_init_vsc_stream_master(struct gf100_gr *); +void gk104_gr_init_rop_active_fbps(struct gf100_gr *); +void gk104_gr_init_ppc_exceptions(struct gf100_gr *); +void gk104_gr_init_sked_hww_esr(struct gf100_gr *); + +void gk110_gr_init_419eb4(struct gf100_gr *); + +void gm107_gr_init_504430(struct gf100_gr *, int, int); +void gm107_gr_init_shader_exceptions(struct gf100_gr *, int, int); +void gm107_gr_init_400054(struct gf100_gr *); + +int gk20a_gr_init(struct gf100_gr *); + +void gm200_gr_oneinit_tiles(struct gf100_gr *); +void gm200_gr_oneinit_sm_id(struct gf100_gr *); +int gm200_gr_rops(struct gf100_gr *); +void gm200_gr_init_num_active_ltcs(struct gf100_gr *); +void gm200_gr_init_ds_hww_esr_2(struct gf100_gr *); + +void gp100_gr_init_rop_active_fbps(struct gf100_gr *); +void gp100_gr_init_fecs_exceptions(struct gf100_gr *); +void gp100_gr_init_shader_exceptions(struct gf100_gr *, int, int); +void gp100_gr_zbc_clear_color(struct gf100_gr *, int); +void gp100_gr_zbc_clear_depth(struct gf100_gr *, int); +extern const struct gf100_gr_func_zbc gp100_gr_zbc; + +void gp102_gr_init_swdx_pes_mask(struct gf100_gr *); +extern const struct gf100_gr_func_zbc gp102_gr_zbc; + +extern const struct gf100_gr_func gp107_gr; + +void gv100_gr_init_419bd8(struct gf100_gr *); +void gv100_gr_init_504430(struct gf100_gr *, int, int); +void gv100_gr_init_shader_exceptions(struct gf100_gr *, int, int); +void gv100_gr_trap_mp(struct gf100_gr *, int, int); + +#define gf100_gr_chan(p) container_of((p), struct gf100_gr_chan, object) +#include + +struct gf100_gr_chan { + struct nvkm_object object; + struct gf100_gr *gr; + struct nvkm_vmm *vmm; + + struct nvkm_memory *mmio; + struct nvkm_vma *mmio_vma; + int mmio_nr; + + struct { + struct nvkm_memory *mem; + struct nvkm_vma *vma; + } data[4]; +}; + +void gf100_gr_ctxctl_debug(struct gf100_gr *); + +u64 gf100_gr_units(struct nvkm_gr *); +void gf100_gr_zbc_init(struct gf100_gr *); + +extern const struct nvkm_object_func gf100_fermi; + +struct gf100_gr_init { + u32 addr; + u8 count; + u32 pitch; + u32 data; +}; + +struct gf100_gr_pack { + const struct gf100_gr_init *init; + u32 type; +}; + +#define pack_for_each_init(init, pack, head) \ + for (pack = head; pack && pack->init; pack++) \ + for (init = pack->init; init && init->count; init++) + +struct gf100_gr_ucode { + struct nvkm_blob code; + struct nvkm_blob data; +}; + +extern struct gf100_gr_ucode gf100_gr_fecs_ucode; +extern struct gf100_gr_ucode gf100_gr_gpccs_ucode; + +extern struct gf100_gr_ucode gk110_gr_fecs_ucode; +extern struct gf100_gr_ucode gk110_gr_gpccs_ucode; + +int gf100_gr_wait_idle(struct gf100_gr *); +void gf100_gr_mmio(struct gf100_gr *, const struct gf100_gr_pack *); +void gf100_gr_icmd(struct gf100_gr *, const struct gf100_gr_pack *); +void gf100_gr_mthd(struct gf100_gr *, const struct gf100_gr_pack *); +int gf100_gr_init_ctxctl(struct gf100_gr *); + +/* register init value lists */ + +extern const struct gf100_gr_init gf100_gr_init_main_0[]; +extern const struct gf100_gr_init gf100_gr_init_fe_0[]; +extern const struct gf100_gr_init gf100_gr_init_pri_0[]; +extern const struct gf100_gr_init gf100_gr_init_rstr2d_0[]; +extern const struct gf100_gr_init gf100_gr_init_pd_0[]; +extern const struct gf100_gr_init gf100_gr_init_ds_0[]; +extern const struct gf100_gr_init gf100_gr_init_scc_0[]; +extern const struct gf100_gr_init gf100_gr_init_prop_0[]; +extern const struct gf100_gr_init gf100_gr_init_gpc_unk_0[]; +extern const struct gf100_gr_init gf100_gr_init_setup_0[]; +extern const struct gf100_gr_init gf100_gr_init_crstr_0[]; +extern const struct gf100_gr_init gf100_gr_init_setup_1[]; +extern const struct gf100_gr_init gf100_gr_init_zcull_0[]; +extern const struct gf100_gr_init gf100_gr_init_gpm_0[]; +extern const struct gf100_gr_init gf100_gr_init_gpc_unk_1[]; +extern const struct gf100_gr_init gf100_gr_init_gcc_0[]; +extern const struct gf100_gr_init gf100_gr_init_tpccs_0[]; +extern const struct gf100_gr_init gf100_gr_init_tex_0[]; +extern const struct gf100_gr_init gf100_gr_init_pe_0[]; +extern const struct gf100_gr_init gf100_gr_init_l1c_0[]; +extern const struct gf100_gr_init gf100_gr_init_wwdx_0[]; +extern const struct gf100_gr_init gf100_gr_init_tpccs_1[]; +extern const struct gf100_gr_init gf100_gr_init_mpc_0[]; +extern const struct gf100_gr_init gf100_gr_init_be_0[]; +extern const struct gf100_gr_init gf100_gr_init_fe_1[]; +extern const struct gf100_gr_init gf100_gr_init_pe_1[]; +void gf100_gr_init_gpc_mmu(struct gf100_gr *); +void gf100_gr_trap_mp(struct gf100_gr *, int, int); +extern const struct nvkm_bitfield gf100_mp_global_error[]; +extern const struct nvkm_enum gf100_mp_warp_error[]; + +extern const struct gf100_gr_init gf104_gr_init_ds_0[]; +extern const struct gf100_gr_init gf104_gr_init_tex_0[]; +extern const struct gf100_gr_init gf104_gr_init_sm_0[]; + +extern const struct gf100_gr_init gf108_gr_init_gpc_unk_0[]; +extern const struct gf100_gr_init gf108_gr_init_setup_1[]; + +extern const struct gf100_gr_init gf119_gr_init_pd_0[]; +extern const struct gf100_gr_init gf119_gr_init_ds_0[]; +extern const struct gf100_gr_init gf119_gr_init_prop_0[]; +extern const struct gf100_gr_init gf119_gr_init_gpm_0[]; +extern const struct gf100_gr_init gf119_gr_init_gpc_unk_1[]; +extern const struct gf100_gr_init gf119_gr_init_tex_0[]; +extern const struct gf100_gr_init gf119_gr_init_sm_0[]; +extern const struct gf100_gr_init gf119_gr_init_fe_1[]; + +extern const struct gf100_gr_init gf117_gr_init_pes_0[]; +extern const struct gf100_gr_init gf117_gr_init_wwdx_0[]; +extern const struct gf100_gr_init gf117_gr_init_cbm_0[]; + +extern const struct gf100_gr_init gk104_gr_init_main_0[]; +extern const struct gf100_gr_init gk104_gr_init_gpc_unk_2[]; +extern const struct gf100_gr_init gk104_gr_init_tpccs_0[]; +extern const struct gf100_gr_init gk104_gr_init_pe_0[]; +extern const struct gf100_gr_init gk104_gr_init_be_0[]; +extern const struct gf100_gr_pack gk104_gr_pack_mmio[]; + +extern const struct gf100_gr_init gk110_gr_init_fe_0[]; +extern const struct gf100_gr_init gk110_gr_init_ds_0[]; +extern const struct gf100_gr_init gk110_gr_init_sked_0[]; +extern const struct gf100_gr_init gk110_gr_init_cwd_0[]; +extern const struct gf100_gr_init gk110_gr_init_gpc_unk_1[]; +extern const struct gf100_gr_init gk110_gr_init_tex_0[]; +extern const struct gf100_gr_init gk110_gr_init_sm_0[]; + +extern const struct gf100_gr_init gk208_gr_init_gpc_unk_0[]; + +extern const struct gf100_gr_init gm107_gr_init_scc_0[]; +extern const struct gf100_gr_init gm107_gr_init_prop_0[]; +extern const struct gf100_gr_init gm107_gr_init_setup_1[]; +extern const struct gf100_gr_init gm107_gr_init_zcull_0[]; +extern const struct gf100_gr_init gm107_gr_init_gpc_unk_1[]; +extern const struct gf100_gr_init gm107_gr_init_tex_0[]; +extern const struct gf100_gr_init gm107_gr_init_l1c_0[]; +extern const struct gf100_gr_init gm107_gr_init_wwdx_0[]; +extern const struct gf100_gr_init gm107_gr_init_cbm_0[]; +void gm107_gr_init_bios(struct gf100_gr *); + +void gm200_gr_init_gpc_mmu(struct gf100_gr *); + +struct gf100_gr_fwif { + int version; + int (*load)(struct gf100_gr *, int ver, const struct gf100_gr_fwif *); + const struct gf100_gr_func *func; + const struct nvkm_acr_lsf_func *fecs; + const struct nvkm_acr_lsf_func *gpccs; +}; + +int gf100_gr_load(struct gf100_gr *, int, const struct gf100_gr_fwif *); +int gf100_gr_nofw(struct gf100_gr *, int, const struct gf100_gr_fwif *); + +int gk20a_gr_load_sw(struct gf100_gr *, const char *path, int ver); + +int gm200_gr_nofw(struct gf100_gr *, int, const struct gf100_gr_fwif *); +int gm200_gr_load(struct gf100_gr *, int, const struct gf100_gr_fwif *); +extern const struct nvkm_acr_lsf_func gm200_gr_gpccs_acr; +extern const struct nvkm_acr_lsf_func gm200_gr_fecs_acr; + +extern const struct nvkm_acr_lsf_func gm20b_gr_fecs_acr; +void gm20b_gr_acr_bld_write(struct nvkm_acr *, u32, struct nvkm_acr_lsfw *); +void gm20b_gr_acr_bld_patch(struct nvkm_acr *, u32, s64); + +extern const struct nvkm_acr_lsf_func gp108_gr_gpccs_acr; +extern const struct nvkm_acr_lsf_func gp108_gr_fecs_acr; + +int gf100_gr_new_(const struct gf100_gr_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_gr **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c new file mode 100644 index 000000000..3acd99c30 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c @@ -0,0 +1,158 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +const struct gf100_gr_init +gf104_gr_init_ds_0[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x00002834 }, + { 0x405908, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf104_gr_init_tex_0[] = { + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf104_gr_init_pe_0[] = { + { 0x41980c, 3, 0x04, 0x00000000 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc5 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419880, 1, 0x04, 0x00000002 }, + {} +}; + +const struct gf100_gr_init +gf104_gr_init_sm_0[] = { + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0e063818 }, + { 0x419ecc, 1, 0x04, 0x0e060e06 }, + { 0x419ed0, 1, 0x04, 0x00003818 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf104_gr_pack_mmio[] = { + { gf100_gr_init_main_0 }, + { gf100_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf100_gr_init_pd_0 }, + { gf104_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gf100_gr_init_prop_0 }, + { gf100_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf100_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf100_gr_init_gpm_0 }, + { gf100_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gf100_gr_init_tpccs_0 }, + { gf104_gr_init_tex_0 }, + { gf104_gr_init_pe_0 }, + { gf100_gr_init_l1c_0 }, + { gf100_gr_init_wwdx_0 }, + { gf100_gr_init_tpccs_1 }, + { gf100_gr_init_mpc_0 }, + { gf104_gr_init_sm_0 }, + { gf100_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct gf100_gr_func +gf104_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gf100_gr_init_vsc_stream_master, + .init_zcull = gf100_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_40601c = gf100_gr_init_40601c, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gf100_gr_init_419eb4, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gf104_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .grctx = &gf104_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, FERMI_MEMORY_TO_MEMORY_FORMAT_A }, + { -1, -1, FERMI_A, &gf100_fermi }, + { -1, -1, FERMI_COMPUTE_A }, + {} + } +}; + +static const struct gf100_gr_fwif +gf104_gr_fwif[] = { + { -1, gf100_gr_load, &gf104_gr }, + { -1, gf100_gr_nofw, &gf104_gr }, + {} +}; + +int +gf104_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gf104_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c new file mode 100644 index 000000000..ab3760e80 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c @@ -0,0 +1,157 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +const struct gf100_gr_init +gf108_gr_init_gpc_unk_0[] = { + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x00000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf108_gr_init_setup_1[] = { + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + {} +}; + +static const struct gf100_gr_init +gf108_gr_init_gpc_unk_1[] = { + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000003 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf108_gr_init_pe_0[] = { + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419810, 1, 0x04, 0x00000000 }, + { 0x419814, 1, 0x04, 0x00000004 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc5 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419880, 1, 0x04, 0x00000002 }, + {} +}; + +static const struct gf100_gr_pack +gf108_gr_pack_mmio[] = { + { gf100_gr_init_main_0 }, + { gf100_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf100_gr_init_pd_0 }, + { gf104_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gf100_gr_init_prop_0 }, + { gf108_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf108_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf100_gr_init_gpm_0 }, + { gf108_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gf100_gr_init_tpccs_0 }, + { gf104_gr_init_tex_0 }, + { gf108_gr_init_pe_0 }, + { gf100_gr_init_l1c_0 }, + { gf100_gr_init_wwdx_0 }, + { gf100_gr_init_tpccs_1 }, + { gf100_gr_init_mpc_0 }, + { gf104_gr_init_sm_0 }, + { gf100_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static void +gf108_gr_init_r405a14(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x405a14, 0x80000000); +} + +static const struct gf100_gr_func +gf108_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_r405a14 = gf108_gr_init_r405a14, + .init_vsc_stream_master = gf100_gr_init_vsc_stream_master, + .init_zcull = gf100_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_40601c = gf100_gr_init_40601c, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gf100_gr_init_419eb4, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gf108_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .grctx = &gf108_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, FERMI_MEMORY_TO_MEMORY_FORMAT_A }, + { -1, -1, FERMI_A, &gf100_fermi }, + { -1, -1, FERMI_B, &gf100_fermi }, + { -1, -1, FERMI_COMPUTE_A }, + {} + } +}; + +static const struct gf100_gr_fwif +gf108_gr_fwif[] = { + { -1, gf100_gr_load, &gf108_gr }, + { -1, gf100_gr_nofw, &gf108_gr }, + {} +}; + +int +gf108_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gf108_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c new file mode 100644 index 000000000..616e2def1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c @@ -0,0 +1,133 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gf110_gr_init_sm_0[] = { + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00001100 }, + { 0x419eac, 1, 0x04, 0x11100f02 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x06060618 }, + { 0x419ed0, 1, 0x04, 0x0eff0e38 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf110_gr_pack_mmio[] = { + { gf100_gr_init_main_0 }, + { gf100_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf100_gr_init_pd_0 }, + { gf100_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gf100_gr_init_prop_0 }, + { gf100_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf108_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf100_gr_init_gpm_0 }, + { gf100_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gf100_gr_init_tpccs_0 }, + { gf100_gr_init_tex_0 }, + { gf100_gr_init_pe_0 }, + { gf100_gr_init_l1c_0 }, + { gf100_gr_init_wwdx_0 }, + { gf100_gr_init_tpccs_1 }, + { gf100_gr_init_mpc_0 }, + { gf110_gr_init_sm_0 }, + { gf100_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + { gf100_gr_init_pe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct gf100_gr_func +gf110_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gf100_gr_init_vsc_stream_master, + .init_zcull = gf100_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_40601c = gf100_gr_init_40601c, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gf100_gr_init_419eb4, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gf110_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .grctx = &gf110_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, FERMI_MEMORY_TO_MEMORY_FORMAT_A }, + { -1, -1, FERMI_A, &gf100_fermi }, + { -1, -1, FERMI_B, &gf100_fermi }, + { -1, -1, FERMI_C, &gf100_fermi }, + { -1, -1, FERMI_COMPUTE_A }, + { -1, -1, FERMI_COMPUTE_B }, + {} + } +}; + +static const struct gf100_gr_fwif +gf110_gr_fwif[] = { + { -1, gf100_gr_load, &gf110_gr }, + { -1, gf100_gr_nofw, &gf110_gr }, + {} +}; + +int +gf110_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gf110_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c new file mode 100644 index 000000000..669e75369 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c @@ -0,0 +1,198 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gf117_gr_init_pe_0[] = { + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc8 }, + { 0x419850, 3, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf117_gr_init_pes_0[] = { + { 0x41be04, 1, 0x04, 0x00000000 }, + { 0x41be08, 1, 0x04, 0x00000004 }, + { 0x41be0c, 1, 0x04, 0x00000000 }, + { 0x41be10, 1, 0x04, 0x003b8bc7 }, + { 0x41be14, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf117_gr_init_wwdx_0[] = { + { 0x41bfd4, 1, 0x04, 0x00800000 }, + { 0x41bfdc, 1, 0x04, 0x00000000 }, + { 0x41bff8, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf117_gr_init_cbm_0[] = { + { 0x41becc, 1, 0x04, 0x00000000 }, + { 0x41bee8, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf117_gr_pack_mmio[] = { + { gf100_gr_init_main_0 }, + { gf100_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf119_gr_init_pd_0 }, + { gf119_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gf119_gr_init_prop_0 }, + { gf108_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf108_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf119_gr_init_gpm_0 }, + { gf119_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gf100_gr_init_tpccs_0 }, + { gf119_gr_init_tex_0 }, + { gf117_gr_init_pe_0 }, + { gf100_gr_init_l1c_0 }, + { gf100_gr_init_mpc_0 }, + { gf119_gr_init_sm_0 }, + { gf117_gr_init_pes_0 }, + { gf117_gr_init_wwdx_0 }, + { gf117_gr_init_cbm_0 }, + { gf100_gr_init_be_0 }, + { gf119_gr_init_fe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +#include "fuc/hubgf117.fuc3.h" + +static struct gf100_gr_ucode +gf117_gr_fecs_ucode = { + .code.data = gf117_grhub_code, + .code.size = sizeof(gf117_grhub_code), + .data.data = gf117_grhub_data, + .data.size = sizeof(gf117_grhub_data), +}; + +#include "fuc/gpcgf117.fuc3.h" + +static struct gf100_gr_ucode +gf117_gr_gpccs_ucode = { + .code.data = gf117_grgpc_code, + .code.size = sizeof(gf117_grgpc_code), + .data.data = gf117_grgpc_data, + .data.size = sizeof(gf117_grgpc_data), +}; + +void +gf117_gr_init_zcull(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, gr->tpc_total); + const u8 tile_nr = ALIGN(gr->tpc_total, 32); + u8 bank[GPC_MAX] = {}, gpc, i, j; + u32 data; + + for (i = 0; i < tile_nr; i += 8) { + for (data = 0, j = 0; j < 8 && i + j < gr->tpc_total; j++) { + data |= bank[gr->tile[i + j]] << (j * 4); + bank[gr->tile[i + j]]++; + } + nvkm_wr32(device, GPC_BCAST(0x0980 + ((i / 8) * 4)), data); + } + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + nvkm_wr32(device, GPC_UNIT(gpc, 0x0914), + gr->screen_tile_row_offset << 8 | gr->tpc_nr[gpc]); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0910), 0x00040000 | + gr->tpc_total); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nvkm_wr32(device, GPC_BCAST(0x3fd4), magicgpc918); +} + +static const struct gf100_gr_func +gf117_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gf100_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_40601c = gf100_gr_init_40601c, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gf100_gr_init_419eb4, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gf117_gr_pack_mmio, + .fecs.ucode = &gf117_gr_fecs_ucode, + .gpccs.ucode = &gf117_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .ppc_nr = 1, + .grctx = &gf117_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, FERMI_MEMORY_TO_MEMORY_FORMAT_A }, + { -1, -1, FERMI_A, &gf100_fermi }, + { -1, -1, FERMI_B, &gf100_fermi }, + { -1, -1, FERMI_C, &gf100_fermi }, + { -1, -1, FERMI_COMPUTE_A }, + { -1, -1, FERMI_COMPUTE_B }, + {} + } +}; + +static const struct gf100_gr_fwif +gf117_gr_fwif[] = { + { -1, gf100_gr_load, &gf117_gr }, + { -1, gf100_gr_nofw, &gf117_gr }, + {} +}; + +int +gf117_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gf117_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c new file mode 100644 index 000000000..5b09bda81 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c @@ -0,0 +1,224 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +const struct gf100_gr_init +gf119_gr_init_pd_0[] = { + { 0x406024, 1, 0x04, 0x00000000 }, + { 0x4064f0, 3, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_gr_init_ds_0[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x00002834 }, + { 0x405908, 1, 0x04, 0x00000000 }, + { 0x405928, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_gr_init_prop_0[] = { + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x4184a0, 3, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_gr_init_gpm_0[] = { + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c64, 2, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418cb4, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_gr_init_gpc_unk_1[] = { + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418d28, 2, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418f20, 2, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000003 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x418e1c, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_gr_init_tex_0[] = { + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x419ab4, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf119_gr_init_pe_0[] = { + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419810, 1, 0x04, 0x00000000 }, + { 0x419814, 1, 0x04, 0x00000004 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x0000a918 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419880, 1, 0x04, 0x00000002 }, + {} +}; + +static const struct gf100_gr_init +gf119_gr_init_wwdx_0[] = { + { 0x419bd4, 1, 0x04, 0x00800000 }, + { 0x419bdc, 1, 0x04, 0x00000000 }, + { 0x419bf8, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gf119_gr_init_tpccs_1[] = { + { 0x419d2c, 1, 0x04, 0x00000000 }, + { 0x419d48, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_gr_init_sm_0[] = { + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x02001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0e063818 }, + { 0x419ecc, 1, 0x04, 0x0e060e06 }, + { 0x419ed0, 1, 0x04, 0x00003818 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gf119_gr_init_fe_1[] = { + { 0x40402c, 1, 0x04, 0x00000000 }, + { 0x4040f0, 1, 0x04, 0x00000000 }, + { 0x404174, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gf119_gr_pack_mmio[] = { + { gf100_gr_init_main_0 }, + { gf100_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf119_gr_init_pd_0 }, + { gf119_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gf119_gr_init_prop_0 }, + { gf108_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf108_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf119_gr_init_gpm_0 }, + { gf119_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gf100_gr_init_tpccs_0 }, + { gf119_gr_init_tex_0 }, + { gf119_gr_init_pe_0 }, + { gf100_gr_init_l1c_0 }, + { gf119_gr_init_wwdx_0 }, + { gf119_gr_init_tpccs_1 }, + { gf100_gr_init_mpc_0 }, + { gf119_gr_init_sm_0 }, + { gf100_gr_init_be_0 }, + { gf119_gr_init_fe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct gf100_gr_func +gf119_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gf100_gr_init_vsc_stream_master, + .init_zcull = gf100_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_40601c = gf100_gr_init_40601c, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gf100_gr_init_419eb4, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gf119_gr_pack_mmio, + .fecs.ucode = &gf100_gr_fecs_ucode, + .gpccs.ucode = &gf100_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .grctx = &gf119_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, FERMI_MEMORY_TO_MEMORY_FORMAT_A }, + { -1, -1, FERMI_A, &gf100_fermi }, + { -1, -1, FERMI_B, &gf100_fermi }, + { -1, -1, FERMI_C, &gf100_fermi }, + { -1, -1, FERMI_COMPUTE_A }, + { -1, -1, FERMI_COMPUTE_B }, + {} + } +}; + +static const struct gf100_gr_fwif +gf119_gr_fwif[] = { + { -1, gf100_gr_load, &gf119_gr }, + { -1, gf100_gr_nofw, &gf119_gr }, + {} +}; + +int +gf119_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gf119_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c new file mode 100644 index 000000000..b680eaa0f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c @@ -0,0 +1,503 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "gk104.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +const struct gf100_gr_init +gk104_gr_init_main_0[] = { + { 0x400080, 1, 0x04, 0x003083c2 }, + { 0x400088, 1, 0x04, 0x0001ffe7 }, + { 0x40008c, 1, 0x04, 0x00000000 }, + { 0x400090, 1, 0x04, 0x00000030 }, + { 0x40013c, 1, 0x04, 0x003901f7 }, + { 0x400140, 1, 0x04, 0x00000100 }, + { 0x400144, 1, 0x04, 0x00000000 }, + { 0x400148, 1, 0x04, 0x00000110 }, + { 0x400138, 1, 0x04, 0x00000000 }, + { 0x400130, 2, 0x04, 0x00000000 }, + { 0x400124, 1, 0x04, 0x00000002 }, + {} +}; + +static const struct gf100_gr_init +gk104_gr_init_ds_0[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x0000ff34 }, + { 0x405908, 1, 0x04, 0x00000000 }, + { 0x405928, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk104_gr_init_sked_0[] = { + { 0x407010, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk104_gr_init_cwd_0[] = { + { 0x405b50, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk104_gr_init_gpc_unk_1[] = { + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418d28, 2, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418f20, 2, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000060 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x418e1c, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk104_gr_init_gpc_unk_2[] = { + { 0x418884, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk104_gr_init_tpccs_0[] = { + { 0x419d0c, 1, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + {} +}; + +const struct gf100_gr_init +gk104_gr_init_pe_0[] = { + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x419850, 1, 0x04, 0x00000004 }, + { 0x419854, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk104_gr_init_l1c_0[] = { + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x01000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00b08bea }, + { 0x419c84, 1, 0x04, 0x00010384 }, + { 0x419cbc, 1, 0x04, 0x28137646 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x00020232 }, + {} +}; + +static const struct gf100_gr_init +gk104_gr_init_sm_0[] = { + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ee4, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00000000 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f74, 1, 0x04, 0x00000555 }, + {} +}; + +const struct gf100_gr_init +gk104_gr_init_be_0[] = { + { 0x40880c, 1, 0x04, 0x00000000 }, + { 0x408850, 1, 0x04, 0x00000004 }, + { 0x408910, 9, 0x04, 0x00000000 }, + { 0x408950, 1, 0x04, 0x00000000 }, + { 0x408954, 1, 0x04, 0x0000ffff }, + { 0x408958, 1, 0x04, 0x00000034 }, + { 0x408984, 1, 0x04, 0x00000000 }, + { 0x408988, 1, 0x04, 0x08040201 }, + { 0x40898c, 1, 0x04, 0x80402010 }, + {} +}; + +const struct gf100_gr_pack +gk104_gr_pack_mmio[] = { + { gk104_gr_init_main_0 }, + { gf100_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf119_gr_init_pd_0 }, + { gk104_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gk104_gr_init_sked_0 }, + { gk104_gr_init_cwd_0 }, + { gf119_gr_init_prop_0 }, + { gf108_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf108_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf119_gr_init_gpm_0 }, + { gk104_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gk104_gr_init_gpc_unk_2 }, + { gk104_gr_init_tpccs_0 }, + { gf119_gr_init_tex_0 }, + { gk104_gr_init_pe_0 }, + { gk104_gr_init_l1c_0 }, + { gf100_gr_init_mpc_0 }, + { gk104_gr_init_sm_0 }, + { gf117_gr_init_pes_0 }, + { gf117_gr_init_wwdx_0 }, + { gf117_gr_init_cbm_0 }, + { gk104_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_main_0[] = { + { 0x4041f0, 1, 0x00004046 }, + { 0x409890, 1, 0x00000045 }, + { 0x4098b0, 1, 0x0000007f }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_rstr2d_0[] = { + { 0x4078c0, 1, 0x00000042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_unk_0[] = { + { 0x406000, 1, 0x00004044 }, + { 0x405860, 1, 0x00004042 }, + { 0x40590c, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gcc_0[] = { + { 0x408040, 1, 0x00004044 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_sked_0[] = { + { 0x407000, 1, 0x00004044 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_unk_1[] = { + { 0x405bf0, 1, 0x00004044 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_ctxctl_0[] = { + { 0x41a890, 1, 0x00000042 }, + { 0x41a8b0, 1, 0x0000007f }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_unk_0[] = { + { 0x418500, 1, 0x00004042 }, + { 0x418608, 1, 0x00004042 }, + { 0x418688, 1, 0x00004042 }, + { 0x418718, 1, 0x00000042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_esetup_0[] = { + { 0x418828, 1, 0x00000044 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_tpbus_0[] = { + { 0x418bbc, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_zcull_0[] = { + { 0x418970, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_tpconf_0[] = { + { 0x418c70, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_unk_1[] = { + { 0x418cf0, 1, 0x00004042 }, + { 0x418d70, 1, 0x00004042 }, + { 0x418f0c, 1, 0x00004042 }, + { 0x418e0c, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_gcc_0[] = { + { 0x419020, 1, 0x00004042 }, + { 0x419038, 1, 0x00000042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_ffb_0[] = { + { 0x418898, 1, 0x00000042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_tex_0[] = { + { 0x419a40, 9, 0x00004042 }, + { 0x419acc, 1, 0x00004047 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_poly_0[] = { + { 0x419868, 1, 0x00000042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_l1c_0[] = { + { 0x419ccc, 3, 0x00000042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_unk_2[] = { + { 0x419c70, 1, 0x00004045 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_mp_0[] = { + { 0x419fd0, 1, 0x00004043 }, + { 0x419fd8, 1, 0x00004049 }, + { 0x419fe0, 2, 0x00004042 }, + { 0x419ff0, 1, 0x00004046 }, + { 0x419ff8, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_gpc_ppc_0[] = { + { 0x41be28, 1, 0x00000042 }, + { 0x41bfe8, 1, 0x00004042 }, + { 0x41bed0, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_rop_zrop_0[] = { + { 0x408810, 2, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_rop_0[] = { + { 0x408a80, 6, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_rop_crop_0[] = { + { 0x4089a8, 1, 0x00004042 }, + { 0x4089b0, 1, 0x00000042 }, + { 0x4089b8, 1, 0x00004042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_clkgate_blcg_init_pxbar_0[] = { + { 0x13c820, 1, 0x0001007f }, + { 0x13cbe0, 1, 0x00000042 }, + {} +}; + +static const struct nvkm_therm_clkgate_pack +gk104_clkgate_pack[] = { + { gk104_clkgate_blcg_init_main_0 }, + { gk104_clkgate_blcg_init_rstr2d_0 }, + { gk104_clkgate_blcg_init_unk_0 }, + { gk104_clkgate_blcg_init_gcc_0 }, + { gk104_clkgate_blcg_init_sked_0 }, + { gk104_clkgate_blcg_init_unk_1 }, + { gk104_clkgate_blcg_init_gpc_ctxctl_0 }, + { gk104_clkgate_blcg_init_gpc_unk_0 }, + { gk104_clkgate_blcg_init_gpc_esetup_0 }, + { gk104_clkgate_blcg_init_gpc_tpbus_0 }, + { gk104_clkgate_blcg_init_gpc_zcull_0 }, + { gk104_clkgate_blcg_init_gpc_tpconf_0 }, + { gk104_clkgate_blcg_init_gpc_unk_1 }, + { gk104_clkgate_blcg_init_gpc_gcc_0 }, + { gk104_clkgate_blcg_init_gpc_ffb_0 }, + { gk104_clkgate_blcg_init_gpc_tex_0 }, + { gk104_clkgate_blcg_init_gpc_poly_0 }, + { gk104_clkgate_blcg_init_gpc_l1c_0 }, + { gk104_clkgate_blcg_init_gpc_unk_2 }, + { gk104_clkgate_blcg_init_gpc_mp_0 }, + { gk104_clkgate_blcg_init_gpc_ppc_0 }, + { gk104_clkgate_blcg_init_rop_zrop_0 }, + { gk104_clkgate_blcg_init_rop_0 }, + { gk104_clkgate_blcg_init_rop_crop_0 }, + { gk104_clkgate_blcg_init_pxbar_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +void +gk104_gr_init_sked_hww_esr(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x407020, 0x40000000); +} + +static void +gk104_gr_init_fecs_exceptions(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, 0x409ffc, 0x00000000); + nvkm_wr32(device, 0x409c14, 0x00003e3e); + nvkm_wr32(device, 0x409c24, 0x000f0001); +} + +void +gk104_gr_init_rop_active_fbps(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 fbp_count = nvkm_rd32(device, 0x120074); + nvkm_mask(device, 0x408850, 0x0000000f, fbp_count); /* zrop */ + nvkm_mask(device, 0x408958, 0x0000000f, fbp_count); /* crop */ +} + +void +gk104_gr_init_ppc_exceptions(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int gpc, ppc; + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++) { + if (!(gr->ppc_mask[gpc] & (1 << ppc))) + continue; + nvkm_wr32(device, PPC_UNIT(gpc, ppc, 0x038), 0xc0000000); + } + } +} + +void +gk104_gr_init_vsc_stream_master(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, GPC_UNIT(0, 0x3018), 0x00000001); +} + +#include "fuc/hubgk104.fuc3.h" + +static struct gf100_gr_ucode +gk104_gr_fecs_ucode = { + .code.data = gk104_grhub_code, + .code.size = sizeof(gk104_grhub_code), + .data.data = gk104_grhub_data, + .data.size = sizeof(gk104_grhub_data), +}; + +#include "fuc/gpcgk104.fuc3.h" + +static struct gf100_gr_ucode +gk104_gr_gpccs_ucode = { + .code.data = gk104_grgpc_code, + .code.size = sizeof(gk104_grgpc_code), + .data.data = gk104_grgpc_data, + .data.size = sizeof(gk104_grgpc_data), +}; + +static const struct gf100_gr_func +gk104_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_rop_active_fbps = gk104_gr_init_rop_active_fbps, + .init_fecs_exceptions = gk104_gr_init_fecs_exceptions, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gf100_gr_init_419eb4, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gk104_gr_pack_mmio, + .fecs.ucode = &gk104_gr_fecs_ucode, + .gpccs.ucode = &gk104_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .ppc_nr = 1, + .grctx = &gk104_grctx, + .clkgate_pack = gk104_clkgate_pack, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_A }, + { -1, -1, KEPLER_A, &gf100_fermi }, + { -1, -1, KEPLER_COMPUTE_A }, + {} + } +}; + +static const struct gf100_gr_fwif +gk104_gr_fwif[] = { + { -1, gf100_gr_load, &gk104_gr }, + { -1, gf100_gr_nofw, &gk104_gr }, + {} +}; + +int +gk104_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gk104_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.h new file mode 100644 index 000000000..a24c17736 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.h @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Lyude Paul + */ +#ifndef __GK104_GR_H__ +#define __GK104_GR_H__ + +#include + +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_main_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_rstr2d_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_unk_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gcc_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_sked_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_unk_1[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_ctxctl_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_unk_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_esetup_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_tpbus_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_zcull_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_tpconf_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_unk_1[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_gcc_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_ffb_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_tex_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_poly_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_l1c_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_unk_2[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_mp_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_gpc_ppc_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_rop_zrop_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_rop_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_rop_crop_0[]; +extern const struct nvkm_therm_clkgate_init gk104_clkgate_blcg_init_pxbar_0[]; + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c new file mode 100644 index 000000000..103e06a77 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c @@ -0,0 +1,399 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "gk104.h" +#include "ctxgf100.h" + +#include + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +const struct gf100_gr_init +gk110_gr_init_fe_0[] = { + { 0x40415c, 1, 0x04, 0x00000000 }, + { 0x404170, 1, 0x04, 0x00000000 }, + { 0x4041b4, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk110_gr_init_ds_0[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x0000ff00 }, + { 0x405908, 1, 0x04, 0x00000000 }, + { 0x405928, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk110_gr_init_sked_0[] = { + { 0x407010, 1, 0x04, 0x00000000 }, + { 0x407040, 1, 0x04, 0x80440424 }, + { 0x407048, 1, 0x04, 0x0000000a }, + {} +}; + +const struct gf100_gr_init +gk110_gr_init_cwd_0[] = { + { 0x405b44, 1, 0x04, 0x00000000 }, + { 0x405b50, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk110_gr_init_gpc_unk_1[] = { + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418d28, 2, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000400 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418f20, 2, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000000 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x418e1c, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk110_gr_init_tex_0[] = { + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419aec, 1, 0x04, 0x00000000 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x419ab4, 1, 0x04, 0x00000000 }, + { 0x419aa8, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk110_gr_init_l1c_0[] = { + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x01000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00b08bea }, + { 0x419c84, 1, 0x04, 0x00010384 }, + { 0x419cbc, 1, 0x04, 0x281b3646 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x00020230 }, + { 0x419ccc, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk110_gr_init_sm_0[] = { + { 0x419e00, 1, 0x04, 0x00000080 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ee4, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00000000 }, + { 0x419eb4, 1, 0x04, 0x00000000 }, + { 0x419ebc, 2, 0x04, 0x00000000 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419ed0, 1, 0x04, 0x00003234 }, + { 0x419f74, 1, 0x04, 0x00015555 }, + { 0x419f80, 4, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gk110_gr_pack_mmio[] = { + { gk104_gr_init_main_0 }, + { gk110_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf119_gr_init_pd_0 }, + { gk110_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gk110_gr_init_sked_0 }, + { gk110_gr_init_cwd_0 }, + { gf119_gr_init_prop_0 }, + { gf108_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf108_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf119_gr_init_gpm_0 }, + { gk110_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gk104_gr_init_gpc_unk_2 }, + { gk104_gr_init_tpccs_0 }, + { gk110_gr_init_tex_0 }, + { gk104_gr_init_pe_0 }, + { gk110_gr_init_l1c_0 }, + { gf100_gr_init_mpc_0 }, + { gk110_gr_init_sm_0 }, + { gf117_gr_init_pes_0 }, + { gf117_gr_init_wwdx_0 }, + { gf117_gr_init_cbm_0 }, + { gk104_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_blcg_init_sked_0[] = { + { 0x407000, 1, 0x00004041 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_blcg_init_gpc_gcc_0[] = { + { 0x419020, 1, 0x00000042 }, + { 0x419038, 1, 0x00000042 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_blcg_init_gpc_l1c_0[] = { + { 0x419cd4, 2, 0x00004042 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_blcg_init_gpc_mp_0[] = { + { 0x419fd0, 1, 0x00004043 }, + { 0x419fd8, 1, 0x00004049 }, + { 0x419fe0, 2, 0x00004042 }, + { 0x419ff0, 1, 0x00000046 }, + { 0x419ff8, 1, 0x00004042 }, + { 0x419f90, 1, 0x00004042 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_main_0[] = { + { 0x4041f4, 1, 0x00000000 }, + { 0x409894, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_unk_0[] = { + { 0x406004, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_sked_0[] = { + { 0x407004, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_ctxctl_0[] = { + { 0x41a894, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_unk_0[] = { + { 0x418504, 1, 0x00000000 }, + { 0x41860c, 1, 0x00000000 }, + { 0x41868c, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_esetup_0[] = { + { 0x41882c, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_zcull_0[] = { + { 0x418974, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_l1c_0[] = { + { 0x419cd8, 2, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_unk_1[] = { + { 0x419c74, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_mp_0[] = { + { 0x419fd4, 1, 0x00004a4a }, + { 0x419fdc, 1, 0x00000014 }, + { 0x419fe4, 1, 0x00000000 }, + { 0x419ff4, 1, 0x00001724 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_gpc_ppc_0[] = { + { 0x41be2c, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_init +gk110_clkgate_slcg_init_pcounter_0[] = { + { 0x1be018, 1, 0x000001ff }, + { 0x1bc018, 1, 0x000001ff }, + { 0x1b8018, 1, 0x000001ff }, + { 0x1b4124, 1, 0x00000000 }, + {} +}; + +static const struct nvkm_therm_clkgate_pack +gk110_clkgate_pack[] = { + { gk104_clkgate_blcg_init_main_0 }, + { gk104_clkgate_blcg_init_rstr2d_0 }, + { gk104_clkgate_blcg_init_unk_0 }, + { gk104_clkgate_blcg_init_gcc_0 }, + { gk110_clkgate_blcg_init_sked_0 }, + { gk104_clkgate_blcg_init_unk_1 }, + { gk104_clkgate_blcg_init_gpc_ctxctl_0 }, + { gk104_clkgate_blcg_init_gpc_unk_0 }, + { gk104_clkgate_blcg_init_gpc_esetup_0 }, + { gk104_clkgate_blcg_init_gpc_tpbus_0 }, + { gk104_clkgate_blcg_init_gpc_zcull_0 }, + { gk104_clkgate_blcg_init_gpc_tpconf_0 }, + { gk104_clkgate_blcg_init_gpc_unk_1 }, + { gk110_clkgate_blcg_init_gpc_gcc_0 }, + { gk104_clkgate_blcg_init_gpc_ffb_0 }, + { gk104_clkgate_blcg_init_gpc_tex_0 }, + { gk104_clkgate_blcg_init_gpc_poly_0 }, + { gk110_clkgate_blcg_init_gpc_l1c_0 }, + { gk104_clkgate_blcg_init_gpc_unk_2 }, + { gk110_clkgate_blcg_init_gpc_mp_0 }, + { gk104_clkgate_blcg_init_gpc_ppc_0 }, + { gk104_clkgate_blcg_init_rop_zrop_0 }, + { gk104_clkgate_blcg_init_rop_0 }, + { gk104_clkgate_blcg_init_rop_crop_0 }, + { gk104_clkgate_blcg_init_pxbar_0 }, + { gk110_clkgate_slcg_init_main_0 }, + { gk110_clkgate_slcg_init_unk_0 }, + { gk110_clkgate_slcg_init_sked_0 }, + { gk110_clkgate_slcg_init_gpc_ctxctl_0 }, + { gk110_clkgate_slcg_init_gpc_unk_0 }, + { gk110_clkgate_slcg_init_gpc_esetup_0 }, + { gk110_clkgate_slcg_init_gpc_zcull_0 }, + { gk110_clkgate_slcg_init_gpc_l1c_0 }, + { gk110_clkgate_slcg_init_gpc_unk_1 }, + { gk110_clkgate_slcg_init_gpc_mp_0 }, + { gk110_clkgate_slcg_init_gpc_ppc_0 }, + { gk110_clkgate_slcg_init_pcounter_0 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +#include "fuc/hubgk110.fuc3.h" + +struct gf100_gr_ucode +gk110_gr_fecs_ucode = { + .code.data = gk110_grhub_code, + .code.size = sizeof(gk110_grhub_code), + .data.data = gk110_grhub_data, + .data.size = sizeof(gk110_grhub_data), +}; + +#include "fuc/gpcgk110.fuc3.h" + +struct gf100_gr_ucode +gk110_gr_gpccs_ucode = { + .code.data = gk110_grgpc_code, + .code.size = sizeof(gk110_grgpc_code), + .data.data = gk110_grgpc_data, + .data.size = sizeof(gk110_grgpc_data), +}; + +void +gk110_gr_init_419eb4(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419eb4, 0x00001000, 0x00001000); + nvkm_mask(device, 0x419eb4, 0x00002000, 0x00002000); + nvkm_mask(device, 0x419eb4, 0x00004000, 0x00004000); + nvkm_mask(device, 0x419eb4, 0x00008000, 0x00008000); + nvkm_mask(device, 0x419eb4, 0x00001000, 0x00000000); + nvkm_mask(device, 0x419eb4, 0x00002000, 0x00000000); + nvkm_mask(device, 0x419eb4, 0x00004000, 0x00000000); + nvkm_mask(device, 0x419eb4, 0x00008000, 0x00000000); +} + +static const struct gf100_gr_func +gk110_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_rop_active_fbps = gk104_gr_init_rop_active_fbps, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gk110_gr_init_419eb4, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gk110_gr_pack_mmio, + .fecs.ucode = &gk110_gr_fecs_ucode, + .gpccs.ucode = &gk110_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .ppc_nr = 2, + .grctx = &gk110_grctx, + .clkgate_pack = gk110_clkgate_pack, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, KEPLER_B, &gf100_fermi }, + { -1, -1, KEPLER_COMPUTE_B }, + {} + } +}; + +static const struct gf100_gr_fwif +gk110_gr_fwif[] = { + { -1, gf100_gr_load, &gk110_gr }, + { -1, gf100_gr_nofw, &gk110_gr }, + {} +}; + +int +gk110_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gk110_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c new file mode 100644 index 000000000..034d0b11a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c @@ -0,0 +1,151 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gk110b_gr_init_l1c_0[] = { + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x09000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00b08bea }, + { 0x419c84, 1, 0x04, 0x00010384 }, + { 0x419cbc, 1, 0x04, 0x281b3646 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x00020230 }, + { 0x419ccc, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk110b_gr_init_sm_0[] = { + { 0x419e00, 1, 0x04, 0x00000080 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ee4, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00000000 }, + { 0x419eb4, 1, 0x04, 0x00000000 }, + { 0x419ebc, 2, 0x04, 0x00000000 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419ed0, 1, 0x04, 0x00002616 }, + { 0x419f74, 1, 0x04, 0x00015555 }, + { 0x419f80, 4, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gk110b_gr_pack_mmio[] = { + { gk104_gr_init_main_0 }, + { gk110_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf119_gr_init_pd_0 }, + { gk110_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gk110_gr_init_sked_0 }, + { gk110_gr_init_cwd_0 }, + { gf119_gr_init_prop_0 }, + { gf108_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gf108_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf119_gr_init_gpm_0 }, + { gk110_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gk104_gr_init_gpc_unk_2 }, + { gk104_gr_init_tpccs_0 }, + { gk110_gr_init_tex_0 }, + { gk104_gr_init_pe_0 }, + { gk110b_gr_init_l1c_0 }, + { gf100_gr_init_mpc_0 }, + { gk110b_gr_init_sm_0 }, + { gf117_gr_init_pes_0 }, + { gf117_gr_init_wwdx_0 }, + { gf117_gr_init_cbm_0 }, + { gk104_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct gf100_gr_func +gk110b_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_rop_active_fbps = gk104_gr_init_rop_active_fbps, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419eb4 = gk110_gr_init_419eb4, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gk110b_gr_pack_mmio, + .fecs.ucode = &gk110_gr_fecs_ucode, + .gpccs.ucode = &gk110_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .ppc_nr = 2, + .grctx = &gk110b_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, KEPLER_B, &gf100_fermi }, + { -1, -1, KEPLER_COMPUTE_B }, + {} + } +}; + +static const struct gf100_gr_fwif +gk110b_gr_fwif[] = { + { -1, gf100_gr_load, &gk110b_gr }, + { -1, gf100_gr_nofw, &gk110b_gr }, + {} +}; + +int +gk110b_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gk110b_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c new file mode 100644 index 000000000..116d682f9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c @@ -0,0 +1,208 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gk208_gr_init_main_0[] = { + { 0x400080, 1, 0x04, 0x003083c2 }, + { 0x400088, 1, 0x04, 0x0001bfe7 }, + { 0x40008c, 1, 0x04, 0x00000000 }, + { 0x400090, 1, 0x04, 0x00000030 }, + { 0x40013c, 1, 0x04, 0x003901f7 }, + { 0x400140, 1, 0x04, 0x00000100 }, + { 0x400144, 1, 0x04, 0x00000000 }, + { 0x400148, 1, 0x04, 0x00000110 }, + { 0x400138, 1, 0x04, 0x00000000 }, + { 0x400130, 2, 0x04, 0x00000000 }, + { 0x400124, 1, 0x04, 0x00000002 }, + {} +}; + +static const struct gf100_gr_init +gk208_gr_init_ds_0[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x00000000 }, + { 0x405908, 1, 0x04, 0x00000000 }, + { 0x405928, 2, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gk208_gr_init_gpc_unk_0[] = { + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x00000000 }, + { 0x418384, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk208_gr_init_setup_1[] = { + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000201 }, + {} +}; + +static const struct gf100_gr_init +gk208_gr_init_tex_0[] = { + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x419ab4, 1, 0x04, 0x00000000 }, + { 0x419aa8, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gk208_gr_init_l1c_0[] = { + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x01000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00b08bea }, + { 0x419c84, 1, 0x04, 0x00010384 }, + { 0x419cbc, 1, 0x04, 0x281b3646 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x00000230 }, + { 0x419ccc, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gk208_gr_pack_mmio[] = { + { gk208_gr_init_main_0 }, + { gk110_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf119_gr_init_pd_0 }, + { gk208_gr_init_ds_0 }, + { gf100_gr_init_scc_0 }, + { gk110_gr_init_sked_0 }, + { gk110_gr_init_cwd_0 }, + { gf119_gr_init_prop_0 }, + { gk208_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gk208_gr_init_setup_1 }, + { gf100_gr_init_zcull_0 }, + { gf119_gr_init_gpm_0 }, + { gk110_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gk104_gr_init_gpc_unk_2 }, + { gk104_gr_init_tpccs_0 }, + { gk208_gr_init_tex_0 }, + { gk104_gr_init_pe_0 }, + { gk208_gr_init_l1c_0 }, + { gf100_gr_init_mpc_0 }, + { gk110_gr_init_sm_0 }, + { gf117_gr_init_pes_0 }, + { gf117_gr_init_wwdx_0 }, + { gf117_gr_init_cbm_0 }, + { gk104_gr_init_be_0 }, + { gf100_gr_init_fe_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +#include "fuc/hubgk208.fuc5.h" + +static struct gf100_gr_ucode +gk208_gr_fecs_ucode = { + .code.data = gk208_grhub_code, + .code.size = sizeof(gk208_grhub_code), + .data.data = gk208_grhub_data, + .data.size = sizeof(gk208_grhub_data), +}; + +#include "fuc/gpcgk208.fuc5.h" + +static struct gf100_gr_ucode +gk208_gr_gpccs_ucode = { + .code.data = gk208_grgpc_code, + .code.size = sizeof(gk208_grgpc_code), + .data.data = gk208_grgpc_data, + .data.size = sizeof(gk208_grgpc_data), +}; + +static const struct gf100_gr_func +gk208_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gf100_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_rop_active_fbps = gk104_gr_init_rop_active_fbps, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_shader_exceptions = gf100_gr_init_shader_exceptions, + .init_400054 = gf100_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gk208_gr_pack_mmio, + .fecs.ucode = &gk208_gr_fecs_ucode, + .gpccs.ucode = &gk208_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .ppc_nr = 1, + .grctx = &gk208_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, KEPLER_B, &gf100_fermi }, + { -1, -1, KEPLER_COMPUTE_B }, + {} + } +}; + +static const struct gf100_gr_fwif +gk208_gr_fwif[] = { + { -1, gf100_gr_load, &gk208_gr }, + { -1, gf100_gr_nofw, &gk208_gr }, + {} +}; + +int +gk208_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gk208_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c new file mode 100644 index 000000000..be0b2cefd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include +#include + +#include + +struct gk20a_fw_av +{ + u32 addr; + u32 data; +}; + +static int +gk20a_gr_av_to_init(struct gf100_gr *gr, const char *path, const char *name, + int ver, struct gf100_gr_pack **ppack) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_blob blob; + struct gf100_gr_init *init; + struct gf100_gr_pack *pack; + int nent; + int ret; + int i; + + ret = nvkm_firmware_load_blob(subdev, path, name, ver, &blob); + if (ret) + return ret; + + nent = (blob.size / sizeof(struct gk20a_fw_av)); + pack = vzalloc((sizeof(*pack) * 2) + (sizeof(*init) * (nent + 1))); + if (!pack) { + ret = -ENOMEM; + goto end; + } + + init = (void *)(pack + 2); + pack[0].init = init; + + for (i = 0; i < nent; i++) { + struct gf100_gr_init *ent = &init[i]; + struct gk20a_fw_av *av = &((struct gk20a_fw_av *)blob.data)[i]; + + ent->addr = av->addr; + ent->data = av->data; + ent->count = 1; + ent->pitch = 1; + } + + *ppack = pack; + +end: + nvkm_blob_dtor(&blob); + return ret; +} + +struct gk20a_fw_aiv +{ + u32 addr; + u32 index; + u32 data; +}; + +static int +gk20a_gr_aiv_to_init(struct gf100_gr *gr, const char *path, const char *name, + int ver, struct gf100_gr_pack **ppack) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_blob blob; + struct gf100_gr_init *init; + struct gf100_gr_pack *pack; + int nent; + int ret; + int i; + + ret = nvkm_firmware_load_blob(subdev, path, name, ver, &blob); + if (ret) + return ret; + + nent = (blob.size / sizeof(struct gk20a_fw_aiv)); + pack = vzalloc((sizeof(*pack) * 2) + (sizeof(*init) * (nent + 1))); + if (!pack) { + ret = -ENOMEM; + goto end; + } + + init = (void *)(pack + 2); + pack[0].init = init; + + for (i = 0; i < nent; i++) { + struct gf100_gr_init *ent = &init[i]; + struct gk20a_fw_aiv *av = &((struct gk20a_fw_aiv *)blob.data)[i]; + + ent->addr = av->addr; + ent->data = av->data; + ent->count = 1; + ent->pitch = 1; + } + + *ppack = pack; + +end: + nvkm_blob_dtor(&blob); + return ret; +} + +static int +gk20a_gr_av_to_method(struct gf100_gr *gr, const char *path, const char *name, + int ver, struct gf100_gr_pack **ppack) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_blob blob; + struct gf100_gr_init *init; + struct gf100_gr_pack *pack; + /* We don't suppose we will initialize more than 16 classes here... */ + static const unsigned int max_classes = 16; + u32 classidx = 0, prevclass = 0; + int nent; + int ret; + int i; + + ret = nvkm_firmware_load_blob(subdev, path, name, ver, &blob); + if (ret) + return ret; + + nent = (blob.size / sizeof(struct gk20a_fw_av)); + + pack = vzalloc((sizeof(*pack) * (max_classes + 1)) + + (sizeof(*init) * (nent + max_classes + 1))); + if (!pack) { + ret = -ENOMEM; + goto end; + } + + init = (void *)(pack + max_classes + 1); + + for (i = 0; i < nent; i++, init++) { + struct gk20a_fw_av *av = &((struct gk20a_fw_av *)blob.data)[i]; + u32 class = av->addr & 0xffff; + u32 addr = (av->addr & 0xffff0000) >> 14; + + if (prevclass != class) { + if (prevclass) /* Add terminator to the method list. */ + init++; + pack[classidx].init = init; + pack[classidx].type = class; + prevclass = class; + if (++classidx >= max_classes) { + vfree(pack); + ret = -ENOSPC; + goto end; + } + } + + init->addr = addr; + init->data = av->data; + init->count = 1; + init->pitch = 1; + } + + *ppack = pack; + +end: + nvkm_blob_dtor(&blob); + return ret; +} + +static int +gk20a_gr_wait_mem_scrubbing(struct gf100_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x40910c) & 0x00000006)) + break; + ) < 0) { + nvkm_error(subdev, "FECS mem scrubbing timeout\n"); + return -ETIMEDOUT; + } + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x41a10c) & 0x00000006)) + break; + ) < 0) { + nvkm_error(subdev, "GPCCS mem scrubbing timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void +gk20a_gr_set_hww_esr_report_mask(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, 0x419e44, 0x1ffffe); + nvkm_wr32(device, 0x419e4c, 0x7f); +} + +int +gk20a_gr_init(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int ret; + + /* Clear SCC RAM */ + nvkm_wr32(device, 0x40802c, 0x1); + + gf100_gr_mmio(gr, gr->sw_nonctx); + + ret = gk20a_gr_wait_mem_scrubbing(gr); + if (ret) + return ret; + + ret = gf100_gr_wait_idle(gr); + if (ret) + return ret; + + /* MMU debug buffer */ + if (gr->func->init_gpc_mmu) + gr->func->init_gpc_mmu(gr); + + /* Set the PE as stream master */ + nvkm_mask(device, 0x503018, 0x1, 0x1); + + /* Zcull init */ + gr->func->init_zcull(gr); + + gr->func->init_rop_active_fbps(gr); + + /* Enable FIFO access */ + nvkm_wr32(device, 0x400500, 0x00010001); + + /* Enable interrupts */ + nvkm_wr32(device, 0x400100, 0xffffffff); + nvkm_wr32(device, 0x40013c, 0xffffffff); + + /* Enable FECS error interrupts */ + nvkm_wr32(device, 0x409c24, 0x000f0000); + + /* Enable hardware warning exceptions */ + nvkm_wr32(device, 0x404000, 0xc0000000); + nvkm_wr32(device, 0x404600, 0xc0000000); + + if (gr->func->set_hww_esr_report_mask) + gr->func->set_hww_esr_report_mask(gr); + + /* Enable TPC exceptions per GPC */ + nvkm_wr32(device, 0x419d0c, 0x2); + nvkm_wr32(device, 0x41ac94, (((1 << gr->tpc_total) - 1) & 0xff) << 16); + + /* Reset and enable all exceptions */ + nvkm_wr32(device, 0x400108, 0xffffffff); + nvkm_wr32(device, 0x400138, 0xffffffff); + nvkm_wr32(device, 0x400118, 0xffffffff); + nvkm_wr32(device, 0x400130, 0xffffffff); + nvkm_wr32(device, 0x40011c, 0xffffffff); + nvkm_wr32(device, 0x400134, 0xffffffff); + + gf100_gr_zbc_init(gr); + + return gf100_gr_init_ctxctl(gr); +} + +static const struct gf100_gr_func +gk20a_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gk20a_gr_init, + .init_zcull = gf117_gr_init_zcull, + .init_rop_active_fbps = gk104_gr_init_rop_active_fbps, + .trap_mp = gf100_gr_trap_mp, + .set_hww_esr_report_mask = gk20a_gr_set_hww_esr_report_mask, + .rops = gf100_gr_rops, + .ppc_nr = 1, + .grctx = &gk20a_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_A }, + { -1, -1, KEPLER_C, &gf100_fermi }, + { -1, -1, KEPLER_COMPUTE_A }, + {} + } +}; + +int +gk20a_gr_load_sw(struct gf100_gr *gr, const char *path, int ver) +{ + if (gk20a_gr_av_to_init(gr, path, "sw_nonctx", ver, &gr->sw_nonctx) || + gk20a_gr_aiv_to_init(gr, path, "sw_ctx", ver, &gr->sw_ctx) || + gk20a_gr_av_to_init(gr, path, "sw_bundle_init", ver, &gr->bundle) || + gk20a_gr_av_to_method(gr, path, "sw_method_init", ver, &gr->method)) + return -ENOENT; + + return 0; +} + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) || IS_ENABLED(CONFIG_ARCH_TEGRA_132_SOC) +MODULE_FIRMWARE("nvidia/gk20a/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gk20a/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gk20a/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gk20a/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gk20a/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gk20a/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gk20a/sw_method_init.bin"); +MODULE_FIRMWARE("nvidia/gk20a/sw_nonctx.bin"); +#endif + +static int +gk20a_gr_load(struct gf100_gr *gr, int ver, const struct gf100_gr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + + if (nvkm_firmware_load_blob(subdev, "", "fecs_inst", ver, + &gr->fecs.inst) || + nvkm_firmware_load_blob(subdev, "", "fecs_data", ver, + &gr->fecs.data) || + nvkm_firmware_load_blob(subdev, "", "gpccs_inst", ver, + &gr->gpccs.inst) || + nvkm_firmware_load_blob(subdev, "", "gpccs_data", ver, + &gr->gpccs.data)) + return -ENOENT; + + gr->firmware = true; + + return gk20a_gr_load_sw(gr, "", ver); +} + +static const struct gf100_gr_fwif +gk20a_gr_fwif[] = { + { 0, gk20a_gr_load, &gk20a_gr }, + {} +}; + +int +gk20a_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gk20a_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c new file mode 100644 index 000000000..310987174 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c @@ -0,0 +1,443 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include +#include +#include +#include +#include + +#include + +/******************************************************************************* + * PGRAPH register lists + ******************************************************************************/ + +static const struct gf100_gr_init +gm107_gr_init_main_0[] = { + { 0x40880c, 1, 0x04, 0x00000000 }, + { 0x408910, 1, 0x04, 0x00000000 }, + { 0x408984, 1, 0x04, 0x00000000 }, + { 0x41a8a0, 1, 0x04, 0x00000000 }, + { 0x400080, 1, 0x04, 0x003003c2 }, + { 0x400088, 1, 0x04, 0x0001bfe7 }, + { 0x40008c, 1, 0x04, 0x00060000 }, + { 0x400090, 1, 0x04, 0x00000030 }, + { 0x40013c, 1, 0x04, 0x003901f3 }, + { 0x400140, 1, 0x04, 0x00000100 }, + { 0x400144, 1, 0x04, 0x00000000 }, + { 0x400148, 1, 0x04, 0x00000110 }, + { 0x400138, 1, 0x04, 0x00000000 }, + { 0x400130, 2, 0x04, 0x00000000 }, + { 0x400124, 1, 0x04, 0x00000002 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_ds_0[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x00000000 }, + { 0x405908, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_scc_0[] = { + { 0x40803c, 1, 0x04, 0x00000010 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_sked_0[] = { + { 0x407010, 1, 0x04, 0x00000000 }, + { 0x407040, 1, 0x04, 0x40440424 }, + { 0x407048, 1, 0x04, 0x0000000a }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_prop_0[] = { + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x4184a0, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_setup_1[] = { + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00010201 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_zcull_0[] = { + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418930, 2, 0x04, 0x00000000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_gpc_unk_1[] = { + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000400 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_tpccs_0[] = { + { 0x419dc4, 1, 0x04, 0x00000000 }, + { 0x419dc8, 1, 0x04, 0x00000501 }, + { 0x419dd0, 1, 0x04, 0x00000000 }, + { 0x419dd4, 1, 0x04, 0x00000100 }, + { 0x419dd8, 1, 0x04, 0x00000001 }, + { 0x419ddc, 1, 0x04, 0x00000002 }, + { 0x419de0, 1, 0x04, 0x00000001 }, + { 0x419d0c, 1, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_tex_0[] = { + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 1, 0x04, 0x00000000 }, + { 0x419acc, 1, 0x04, 0x000000ff }, + { 0x419ac0, 1, 0x04, 0x00000000 }, + { 0x419aa8, 2, 0x04, 0x00000000 }, + { 0x419ad0, 2, 0x04, 0x00000000 }, + { 0x419ae0, 2, 0x04, 0x00000000 }, + { 0x419af0, 4, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_pe_0[] = { + { 0x419900, 1, 0x04, 0x000000ff }, + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x419838, 1, 0x04, 0x000000ff }, + { 0x419850, 1, 0x04, 0x00000004 }, + { 0x419854, 2, 0x04, 0x00000000 }, + { 0x419894, 3, 0x04, 0x00100401 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_l1c_0[] = { + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_sm_0[] = { + { 0x419e30, 1, 0x04, 0x000000ff }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ee4, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x01000000 }, + { 0x419ee8, 1, 0x04, 0x00000091 }, + { 0x419eb4, 1, 0x04, 0x00000000 }, + { 0x419ebc, 2, 0x04, 0x00000000 }, + { 0x419edc, 1, 0x04, 0x000c1810 }, + { 0x419ed8, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00000000 }, + { 0x419f74, 1, 0x04, 0x00005155 }, + { 0x419f80, 4, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_l1c_1[] = { + { 0x419ccc, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x3f006022 }, + { 0x419c88, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_pes_0[] = { + { 0x41be50, 1, 0x04, 0x000000ff }, + { 0x41be04, 1, 0x04, 0x00000000 }, + { 0x41be08, 1, 0x04, 0x00000004 }, + { 0x41be0c, 1, 0x04, 0x00000008 }, + { 0x41be10, 1, 0x04, 0x0e3b8bc7 }, + { 0x41be14, 2, 0x04, 0x00000000 }, + { 0x41be3c, 5, 0x04, 0x00100401 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_wwdx_0[] = { + { 0x41bfd4, 1, 0x04, 0x00800000 }, + { 0x41bfdc, 1, 0x04, 0x00000000 }, + {} +}; + +const struct gf100_gr_init +gm107_gr_init_cbm_0[] = { + { 0x41becc, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_be_0[] = { + { 0x408890, 1, 0x04, 0x000000ff }, + { 0x408850, 1, 0x04, 0x00000004 }, + { 0x408878, 1, 0x04, 0x00c81603 }, + { 0x40887c, 1, 0x04, 0x80543432 }, + { 0x408880, 1, 0x04, 0x0010581e }, + { 0x408884, 1, 0x04, 0x00001205 }, + { 0x408974, 1, 0x04, 0x000000ff }, + { 0x408914, 8, 0x04, 0x00000000 }, + { 0x408950, 1, 0x04, 0x00000000 }, + { 0x408954, 1, 0x04, 0x0000ffff }, + { 0x408958, 1, 0x04, 0x00000034 }, + { 0x40895c, 1, 0x04, 0x8531a003 }, + { 0x408960, 1, 0x04, 0x0561985a }, + { 0x408964, 1, 0x04, 0x04e15c4f }, + { 0x408968, 1, 0x04, 0x02808833 }, + { 0x40896c, 1, 0x04, 0x01f02438 }, + { 0x408970, 1, 0x04, 0x00012c00 }, + { 0x408988, 1, 0x04, 0x08040201 }, + { 0x40898c, 1, 0x04, 0x80402010 }, + {} +}; + +static const struct gf100_gr_init +gm107_gr_init_sm_1[] = { + { 0x419e5c, 1, 0x04, 0x00000000 }, + { 0x419e58, 1, 0x04, 0x00000000 }, + {} +}; + +static const struct gf100_gr_pack +gm107_gr_pack_mmio[] = { + { gm107_gr_init_main_0 }, + { gk110_gr_init_fe_0 }, + { gf100_gr_init_pri_0 }, + { gf100_gr_init_rstr2d_0 }, + { gf100_gr_init_pd_0 }, + { gm107_gr_init_ds_0 }, + { gm107_gr_init_scc_0 }, + { gm107_gr_init_sked_0 }, + { gk110_gr_init_cwd_0 }, + { gm107_gr_init_prop_0 }, + { gk208_gr_init_gpc_unk_0 }, + { gf100_gr_init_setup_0 }, + { gf100_gr_init_crstr_0 }, + { gm107_gr_init_setup_1 }, + { gm107_gr_init_zcull_0 }, + { gf100_gr_init_gpm_0 }, + { gm107_gr_init_gpc_unk_1 }, + { gf100_gr_init_gcc_0 }, + { gk104_gr_init_gpc_unk_2 }, + { gm107_gr_init_tpccs_0 }, + { gm107_gr_init_tex_0 }, + { gm107_gr_init_pe_0 }, + { gm107_gr_init_l1c_0 }, + { gf100_gr_init_mpc_0 }, + { gm107_gr_init_sm_0 }, + { gm107_gr_init_l1c_1 }, + { gm107_gr_init_pes_0 }, + { gm107_gr_init_wwdx_0 }, + { gm107_gr_init_cbm_0 }, + { gm107_gr_init_be_0 }, + { gm107_gr_init_sm_1 }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +void +gm107_gr_init_400054(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x400054, 0x2c350f63); +} + +void +gm107_gr_init_shader_exceptions(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x644), 0x00dffffe); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x64c), 0x00000005); +} + +void +gm107_gr_init_504430(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x430), 0xc0000000); +} + +static void +gm107_gr_init_bios_2(struct gf100_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct bit_entry bit_P; + if (!bit_entry(bios, 'P', &bit_P) && + bit_P.version == 2 && bit_P.length >= 0x2c) { + u32 data = nvbios_rd32(bios, bit_P.offset + 0x28); + if (data) { + u8 ver = nvbios_rd08(bios, data + 0x00); + u8 hdr = nvbios_rd08(bios, data + 0x01); + if (ver == 0x20 && hdr >= 8) { + data = nvbios_rd32(bios, data + 0x04); + if (data) { + u32 save = nvkm_rd32(device, 0x619444); + nvbios_init(subdev, data); + nvkm_wr32(device, 0x619444, save); + } + } + } + } +} + +void +gm107_gr_init_bios(struct gf100_gr *gr) +{ + static const struct { + u32 ctrl; + u32 data; + } regs[] = { + { 0x419ed8, 0x419ee0 }, + { 0x419ad0, 0x419ad4 }, + { 0x419ae0, 0x419ae4 }, + { 0x419af0, 0x419af4 }, + { 0x419af8, 0x419afc }, + }; + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvbios_P0260E infoE; + struct nvbios_P0260X infoX; + int E = -1, X; + u8 ver, hdr; + + while (nvbios_P0260Ep(bios, ++E, &ver, &hdr, &infoE)) { + if (X = -1, E < ARRAY_SIZE(regs)) { + nvkm_wr32(device, regs[E].ctrl, infoE.data); + while (nvbios_P0260Xp(bios, ++X, &ver, &hdr, &infoX)) + nvkm_wr32(device, regs[E].data, infoX.data); + } + } +} + +static void +gm107_gr_init_gpc_mmu(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nvkm_fb *fb = device->fb; + + nvkm_wr32(device, GPC_BCAST(0x0880), 0x00000000); + nvkm_wr32(device, GPC_BCAST(0x0890), 0x00000000); + nvkm_wr32(device, GPC_BCAST(0x0894), 0x00000000); + nvkm_wr32(device, GPC_BCAST(0x08b4), nvkm_memory_addr(fb->mmu_wr) >> 8); + nvkm_wr32(device, GPC_BCAST(0x08b8), nvkm_memory_addr(fb->mmu_rd) >> 8); +} + +#include "fuc/hubgm107.fuc5.h" + +static struct gf100_gr_ucode +gm107_gr_fecs_ucode = { + .code.data = gm107_grhub_code, + .code.size = sizeof(gm107_grhub_code), + .data.data = gm107_grhub_data, + .data.size = sizeof(gm107_grhub_data), +}; + +#include "fuc/gpcgm107.fuc5.h" + +static struct gf100_gr_ucode +gm107_gr_gpccs_ucode = { + .code.data = gm107_grgpc_code, + .code.size = sizeof(gm107_grgpc_code), + .data.data = gm107_grgpc_data, + .data.size = sizeof(gm107_grgpc_data), +}; + +static const struct gf100_gr_func +gm107_gr = { + .oneinit_tiles = gf100_gr_oneinit_tiles, + .oneinit_sm_id = gf100_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gm107_gr_init_gpc_mmu, + .init_bios = gm107_gr_init_bios, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_rop_active_fbps = gk104_gr_init_rop_active_fbps, + .init_bios_2 = gm107_gr_init_bios_2, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_504430 = gm107_gr_init_504430, + .init_shader_exceptions = gm107_gr_init_shader_exceptions, + .init_400054 = gm107_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .mmio = gm107_gr_pack_mmio, + .fecs.ucode = &gm107_gr_fecs_ucode, + .gpccs.ucode = &gm107_gr_gpccs_ucode, + .rops = gf100_gr_rops, + .ppc_nr = 2, + .grctx = &gm107_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, MAXWELL_A, &gf100_fermi }, + { -1, -1, MAXWELL_COMPUTE_A }, + {} + } +}; + +static const struct gf100_gr_fwif +gm107_gr_fwif[] = { + { -1, gf100_gr_load, &gm107_gr }, + { -1, gf100_gr_nofw, &gm107_gr }, + {} +}; + +int +gm107_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gm107_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm200.c new file mode 100644 index 000000000..385cfd91b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm200.c @@ -0,0 +1,293 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include +#include + +#include + +#include + +int +gm200_gr_nofw(struct gf100_gr *gr, int ver, const struct gf100_gr_fwif *fwif) +{ + nvkm_warn(&gr->base.engine.subdev, "firmware unavailable\n"); + return -ENODEV; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static void +gm200_gr_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust) +{ + struct flcn_bl_dmem_desc_v1 hdr; + nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr)); + hdr.code_dma_base = hdr.code_dma_base + adjust; + hdr.data_dma_base = hdr.data_dma_base + adjust; + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); + flcn_bl_dmem_desc_v1_dump(&acr->subdev, &hdr); +} + +static void +gm200_gr_acr_bld_write(struct nvkm_acr *acr, u32 bld, + struct nvkm_acr_lsfw *lsfw) +{ + const u64 base = lsfw->offset.img + lsfw->app_start_offset; + const u64 code = base + lsfw->app_resident_code_offset; + const u64 data = base + lsfw->app_resident_data_offset; + const struct flcn_bl_dmem_desc_v1 hdr = { + .ctx_dma = FALCON_DMAIDX_UCODE, + .code_dma_base = code, + .non_sec_code_off = lsfw->app_resident_code_offset, + .non_sec_code_size = lsfw->app_resident_code_size, + .code_entry_point = lsfw->app_imem_entry, + .data_dma_base = data, + .data_size = lsfw->app_resident_data_size, + }; + + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); +} + +const struct nvkm_acr_lsf_func +gm200_gr_gpccs_acr = { + .flags = NVKM_ACR_LSF_FORCE_PRIV_LOAD, + .bld_size = sizeof(struct flcn_bl_dmem_desc_v1), + .bld_write = gm200_gr_acr_bld_write, + .bld_patch = gm200_gr_acr_bld_patch, +}; + +const struct nvkm_acr_lsf_func +gm200_gr_fecs_acr = { + .bld_size = sizeof(struct flcn_bl_dmem_desc_v1), + .bld_write = gm200_gr_acr_bld_write, + .bld_patch = gm200_gr_acr_bld_patch, +}; + +int +gm200_gr_rops(struct gf100_gr *gr) +{ + return nvkm_rd32(gr->base.engine.subdev.device, 0x12006c); +} + +void +gm200_gr_init_ds_hww_esr_2(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, 0x405848, 0xc0000000); + nvkm_mask(device, 0x40584c, 0x00000001, 0x00000001); +} + +void +gm200_gr_init_num_active_ltcs(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, GPC_BCAST(0x08ac), nvkm_rd32(device, 0x100800)); + nvkm_wr32(device, GPC_BCAST(0x033c), nvkm_rd32(device, 0x100804)); +} + +void +gm200_gr_init_gpc_mmu(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x418880, nvkm_rd32(device, 0x100c80) & 0xf0001fff); + nvkm_wr32(device, 0x418890, 0x00000000); + nvkm_wr32(device, 0x418894, 0x00000000); + + nvkm_wr32(device, 0x4188b4, nvkm_rd32(device, 0x100cc8)); + nvkm_wr32(device, 0x4188b8, nvkm_rd32(device, 0x100ccc)); + nvkm_wr32(device, 0x4188b0, nvkm_rd32(device, 0x100cc4)); +} + +static void +gm200_gr_init_rop_active_fbps(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 fbp_count = nvkm_rd32(device, 0x12006c); + nvkm_mask(device, 0x408850, 0x0000000f, fbp_count); /* zrop */ + nvkm_mask(device, 0x408958, 0x0000000f, fbp_count); /* crop */ +} + +static u8 +gm200_gr_tile_map_6_24[] = { + 0, 1, 2, 3, 4, 5, 3, 4, 5, 0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 0, 1, 2, +}; + +static u8 +gm200_gr_tile_map_4_16[] = { + 0, 1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2, 1, 2, 3, 0, +}; + +static u8 +gm200_gr_tile_map_2_8[] = { + 0, 1, 1, 0, 0, 1, 1, 0, +}; + +void +gm200_gr_oneinit_sm_id(struct gf100_gr *gr) +{ + /*XXX: There's a different algorithm here I've not yet figured out. */ + gf100_gr_oneinit_sm_id(gr); +} + +void +gm200_gr_oneinit_tiles(struct gf100_gr *gr) +{ + /*XXX: Not sure what this is about. The algorithm from NVGPU + * seems to work for all boards I tried from earlier (and + * later) GPUs except in these specific configurations. + * + * Let's just hardcode them for now. + */ + if (gr->gpc_nr == 2 && gr->tpc_total == 8) { + memcpy(gr->tile, gm200_gr_tile_map_2_8, gr->tpc_total); + gr->screen_tile_row_offset = 1; + } else + if (gr->gpc_nr == 4 && gr->tpc_total == 16) { + memcpy(gr->tile, gm200_gr_tile_map_4_16, gr->tpc_total); + gr->screen_tile_row_offset = 4; + } else + if (gr->gpc_nr == 6 && gr->tpc_total == 24) { + memcpy(gr->tile, gm200_gr_tile_map_6_24, gr->tpc_total); + gr->screen_tile_row_offset = 5; + } else { + gf100_gr_oneinit_tiles(gr); + } +} + +static const struct gf100_gr_func +gm200_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gm200_gr_init_gpc_mmu, + .init_bios = gm107_gr_init_bios, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gm200_gr_init_num_active_ltcs, + .init_rop_active_fbps = gm200_gr_init_rop_active_fbps, + .init_fecs_exceptions = gf100_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_504430 = gm107_gr_init_504430, + .init_shader_exceptions = gm107_gr_init_shader_exceptions, + .init_400054 = gm107_gr_init_400054, + .trap_mp = gf100_gr_trap_mp, + .rops = gm200_gr_rops, + .tpc_nr = 4, + .ppc_nr = 2, + .grctx = &gm200_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, MAXWELL_B, &gf100_fermi }, + { -1, -1, MAXWELL_COMPUTE_B }, + {} + } +}; + +int +gm200_gr_load(struct gf100_gr *gr, int ver, const struct gf100_gr_fwif *fwif) +{ + int ret; + + ret = nvkm_acr_lsfw_load_bl_inst_data_sig(&gr->base.engine.subdev, + &gr->fecs.falcon, + NVKM_ACR_LSF_FECS, + "gr/fecs_", ver, fwif->fecs); + if (ret) + return ret; + + ret = nvkm_acr_lsfw_load_bl_inst_data_sig(&gr->base.engine.subdev, + &gr->gpccs.falcon, + NVKM_ACR_LSF_GPCCS, + "gr/gpccs_", ver, + fwif->gpccs); + if (ret) + return ret; + + gr->firmware = true; + + return gk20a_gr_load_sw(gr, "gr/", ver); +} + +MODULE_FIRMWARE("nvidia/gm200/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gm200/gr/sw_method_init.bin"); + +MODULE_FIRMWARE("nvidia/gm204/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gm204/gr/sw_method_init.bin"); + +MODULE_FIRMWARE("nvidia/gm206/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gm206/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +gm200_gr_fwif[] = { + { 0, gm200_gr_load, &gm200_gr, &gm200_gr_fecs_acr, &gm200_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gm200_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gm200_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm20b.c new file mode 100644 index 000000000..ec1c46e47 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm20b.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include +#include +#include + +#include + +#include + +void +gm20b_gr_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust) +{ + struct flcn_bl_dmem_desc hdr; + u64 addr; + + nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr)); + addr = ((u64)hdr.code_dma_base1 << 40 | hdr.code_dma_base << 8); + hdr.code_dma_base = lower_32_bits((addr + adjust) >> 8); + hdr.code_dma_base1 = upper_32_bits((addr + adjust) >> 8); + addr = ((u64)hdr.data_dma_base1 << 40 | hdr.data_dma_base << 8); + hdr.data_dma_base = lower_32_bits((addr + adjust) >> 8); + hdr.data_dma_base1 = upper_32_bits((addr + adjust) >> 8); + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); + + flcn_bl_dmem_desc_dump(&acr->subdev, &hdr); +} + +void +gm20b_gr_acr_bld_write(struct nvkm_acr *acr, u32 bld, + struct nvkm_acr_lsfw *lsfw) +{ + const u64 base = lsfw->offset.img + lsfw->app_start_offset; + const u64 code = (base + lsfw->app_resident_code_offset) >> 8; + const u64 data = (base + lsfw->app_resident_data_offset) >> 8; + const struct flcn_bl_dmem_desc hdr = { + .ctx_dma = FALCON_DMAIDX_UCODE, + .code_dma_base = lower_32_bits(code), + .non_sec_code_off = lsfw->app_resident_code_offset, + .non_sec_code_size = lsfw->app_resident_code_size, + .code_entry_point = lsfw->app_imem_entry, + .data_dma_base = lower_32_bits(data), + .data_size = lsfw->app_resident_data_size, + .code_dma_base1 = upper_32_bits(code), + .data_dma_base1 = upper_32_bits(data), + }; + + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); +} + +const struct nvkm_acr_lsf_func +gm20b_gr_fecs_acr = { + .bld_size = sizeof(struct flcn_bl_dmem_desc), + .bld_write = gm20b_gr_acr_bld_write, + .bld_patch = gm20b_gr_acr_bld_patch, +}; + +static void +gm20b_gr_init_gpc_mmu(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 val; + + /* Bypass MMU check for non-secure boot */ + if (!device->acr) { + nvkm_wr32(device, 0x100ce4, 0xffffffff); + + if (nvkm_rd32(device, 0x100ce4) != 0xffffffff) + nvdev_warn(device, + "cannot bypass secure boot - expect failure soon!\n"); + } + + val = nvkm_rd32(device, 0x100c80); + val &= 0xf000187f; + nvkm_wr32(device, 0x418880, val); + nvkm_wr32(device, 0x418890, 0); + nvkm_wr32(device, 0x418894, 0); + + nvkm_wr32(device, 0x4188b0, nvkm_rd32(device, 0x100cc4)); + nvkm_wr32(device, 0x4188b4, nvkm_rd32(device, 0x100cc8)); + nvkm_wr32(device, 0x4188b8, nvkm_rd32(device, 0x100ccc)); + + nvkm_wr32(device, 0x4188ac, nvkm_rd32(device, 0x100800)); +} + +static void +gm20b_gr_set_hww_esr_report_mask(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, 0x419e44, 0xdffffe); + nvkm_wr32(device, 0x419e4c, 0x5); +} + +static const struct gf100_gr_func +gm20b_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gk20a_gr_init, + .init_zcull = gf117_gr_init_zcull, + .init_gpc_mmu = gm20b_gr_init_gpc_mmu, + .init_rop_active_fbps = gk104_gr_init_rop_active_fbps, + .trap_mp = gf100_gr_trap_mp, + .set_hww_esr_report_mask = gm20b_gr_set_hww_esr_report_mask, + .rops = gm200_gr_rops, + .ppc_nr = 1, + .grctx = &gm20b_grctx, + .zbc = &gf100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, MAXWELL_B, &gf100_fermi }, + { -1, -1, MAXWELL_COMPUTE_B }, + {} + } +}; + +static int +gm20b_gr_load(struct gf100_gr *gr, int ver, const struct gf100_gr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + int ret; + + ret = nvkm_acr_lsfw_load_bl_inst_data_sig(subdev, &gr->fecs.falcon, + NVKM_ACR_LSF_FECS, + "gr/fecs_", ver, fwif->fecs); + if (ret) + return ret; + + + if (nvkm_firmware_load_blob(subdev, "gr/", "gpccs_inst", ver, + &gr->gpccs.inst) || + nvkm_firmware_load_blob(subdev, "gr/", "gpccs_data", ver, + &gr->gpccs.data)) + return -ENOENT; + + gr->firmware = true; + + return gk20a_gr_load_sw(gr, "gr/", ver); +} + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +MODULE_FIRMWARE("nvidia/gm20b/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gm20b/gr/sw_method_init.bin"); +#endif + +static const struct gf100_gr_fwif +gm20b_gr_fwif[] = { + { 0, gm20b_gr_load, &gm20b_gr, &gm20b_gr_fecs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gm20b_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gm20b_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c new file mode 100644 index 000000000..0550dd6f4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c @@ -0,0 +1,162 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ +void +gp100_gr_zbc_clear_color(struct gf100_gr *gr, int zbc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const int znum = zbc - 1; + const u32 zoff = znum * 4; + + if (gr->zbc_color[zbc].format) { + nvkm_wr32(device, 0x418010 + zoff, gr->zbc_color[zbc].ds[0]); + nvkm_wr32(device, 0x41804c + zoff, gr->zbc_color[zbc].ds[1]); + nvkm_wr32(device, 0x418088 + zoff, gr->zbc_color[zbc].ds[2]); + nvkm_wr32(device, 0x4180c4 + zoff, gr->zbc_color[zbc].ds[3]); + } + + nvkm_mask(device, 0x418100 + ((znum / 4) * 4), + 0x0000007f << ((znum % 4) * 7), + gr->zbc_color[zbc].format << ((znum % 4) * 7)); +} + +void +gp100_gr_zbc_clear_depth(struct gf100_gr *gr, int zbc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const int znum = zbc - 1; + const u32 zoff = znum * 4; + + if (gr->zbc_depth[zbc].format) + nvkm_wr32(device, 0x418110 + zoff, gr->zbc_depth[zbc].ds); + nvkm_mask(device, 0x41814c + ((znum / 4) * 4), + 0x0000007f << ((znum % 4) * 7), + gr->zbc_depth[zbc].format << ((znum % 4) * 7)); +} + +const struct gf100_gr_func_zbc +gp100_gr_zbc = { + .clear_color = gp100_gr_zbc_clear_color, + .clear_depth = gp100_gr_zbc_clear_depth, +}; + +void +gp100_gr_init_shader_exceptions(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x644), 0x00dffffe); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x64c), 0x00000105); +} + +static void +gp100_gr_init_419c9c(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419c9c, 0x00010000, 0x00010000); + nvkm_mask(device, 0x419c9c, 0x00020000, 0x00020000); +} + +void +gp100_gr_init_fecs_exceptions(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x409c24, 0x000f0002); +} + +void +gp100_gr_init_rop_active_fbps(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + /*XXX: otherwise identical to gm200 aside from mask.. do everywhere? */ + const u32 fbp_count = nvkm_rd32(device, 0x12006c) & 0x0000000f; + nvkm_mask(device, 0x408850, 0x0000000f, fbp_count); /* zrop */ + nvkm_mask(device, 0x408958, 0x0000000f, fbp_count); /* crop */ +} + +static const struct gf100_gr_func +gp100_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gm200_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gm200_gr_init_num_active_ltcs, + .init_rop_active_fbps = gp100_gr_init_rop_active_fbps, + .init_fecs_exceptions = gp100_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_419c9c = gp100_gr_init_419c9c, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_504430 = gm107_gr_init_504430, + .init_shader_exceptions = gp100_gr_init_shader_exceptions, + .trap_mp = gf100_gr_trap_mp, + .rops = gm200_gr_rops, + .gpc_nr = 6, + .tpc_nr = 5, + .ppc_nr = 2, + .grctx = &gp100_grctx, + .zbc = &gp100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, PASCAL_A, &gf100_fermi }, + { -1, -1, PASCAL_COMPUTE_A }, + {} + } +}; + +MODULE_FIRMWARE("nvidia/gp100/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gp100/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +gp100_gr_fwif[] = { + { 0, gm200_gr_load, &gp100_gr, &gm200_gr_fecs_acr, &gm200_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gp100_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gp100_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c new file mode 100644 index 000000000..5b001f374 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c @@ -0,0 +1,158 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +static void +gp102_gr_zbc_clear_stencil(struct gf100_gr *gr, int zbc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const int znum = zbc - 1; + const u32 zoff = znum * 4; + + if (gr->zbc_stencil[zbc].format) + nvkm_wr32(device, 0x41815c + zoff, gr->zbc_stencil[zbc].ds); + nvkm_mask(device, 0x418198 + ((znum / 4) * 4), + 0x0000007f << ((znum % 4) * 7), + gr->zbc_stencil[zbc].format << ((znum % 4) * 7)); +} + +static int +gp102_gr_zbc_stencil_get(struct gf100_gr *gr, int format, + const u32 ds, const u32 l2) +{ + struct nvkm_ltc *ltc = gr->base.engine.subdev.device->ltc; + int zbc = -ENOSPC, i; + + for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) { + if (gr->zbc_stencil[i].format) { + if (gr->zbc_stencil[i].format != format) + continue; + if (gr->zbc_stencil[i].ds != ds) + continue; + if (gr->zbc_stencil[i].l2 != l2) { + WARN_ON(1); + return -EINVAL; + } + return i; + } else { + zbc = (zbc < 0) ? i : zbc; + } + } + + if (zbc < 0) + return zbc; + + gr->zbc_stencil[zbc].format = format; + gr->zbc_stencil[zbc].ds = ds; + gr->zbc_stencil[zbc].l2 = l2; + nvkm_ltc_zbc_stencil_get(ltc, zbc, l2); + gr->func->zbc->clear_stencil(gr, zbc); + return zbc; +} + +const struct gf100_gr_func_zbc +gp102_gr_zbc = { + .clear_color = gp100_gr_zbc_clear_color, + .clear_depth = gp100_gr_zbc_clear_depth, + .stencil_get = gp102_gr_zbc_stencil_get, + .clear_stencil = gp102_gr_zbc_clear_stencil, +}; + +void +gp102_gr_init_swdx_pes_mask(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 mask = 0, data, gpc; + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + data = nvkm_rd32(device, GPC_UNIT(gpc, 0x0c50)) & 0x0000000f; + mask |= data << (gpc * 4); + } + + nvkm_wr32(device, 0x4181d0, mask); +} + +static const struct gf100_gr_func +gp102_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gm200_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gm200_gr_init_num_active_ltcs, + .init_rop_active_fbps = gp100_gr_init_rop_active_fbps, + .init_swdx_pes_mask = gp102_gr_init_swdx_pes_mask, + .init_fecs_exceptions = gp100_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_504430 = gm107_gr_init_504430, + .init_shader_exceptions = gp100_gr_init_shader_exceptions, + .trap_mp = gf100_gr_trap_mp, + .rops = gm200_gr_rops, + .gpc_nr = 6, + .tpc_nr = 5, + .ppc_nr = 3, + .grctx = &gp102_grctx, + .zbc = &gp102_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, PASCAL_B, &gf100_fermi }, + { -1, -1, PASCAL_COMPUTE_B }, + {} + } +}; + +MODULE_FIRMWARE("nvidia/gp102/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gp102/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +gp102_gr_fwif[] = { + { 0, gm200_gr_load, &gp102_gr, &gm200_gr_fecs_acr, &gm200_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gp102_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gp102_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp104.c new file mode 100644 index 000000000..2655574ec --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp104.c @@ -0,0 +1,99 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +static const struct gf100_gr_func +gp104_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gm200_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gm200_gr_init_num_active_ltcs, + .init_rop_active_fbps = gp100_gr_init_rop_active_fbps, + .init_swdx_pes_mask = gp102_gr_init_swdx_pes_mask, + .init_fecs_exceptions = gp100_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_504430 = gm107_gr_init_504430, + .init_shader_exceptions = gp100_gr_init_shader_exceptions, + .trap_mp = gf100_gr_trap_mp, + .rops = gm200_gr_rops, + .gpc_nr = 6, + .tpc_nr = 5, + .ppc_nr = 3, + .grctx = &gp104_grctx, + .zbc = &gp102_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, PASCAL_B, &gf100_fermi }, + { -1, -1, PASCAL_COMPUTE_B }, + {} + } +}; + +MODULE_FIRMWARE("nvidia/gp104/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gp104/gr/sw_method_init.bin"); + +MODULE_FIRMWARE("nvidia/gp106/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gp106/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +gp104_gr_fwif[] = { + { 0, gm200_gr_load, &gp104_gr, &gm200_gr_fecs_acr, &gm200_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gp104_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gp104_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp107.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp107.c new file mode 100644 index 000000000..adabc04d4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp107.c @@ -0,0 +1,88 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +const struct gf100_gr_func +gp107_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gm200_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gm200_gr_init_num_active_ltcs, + .init_rop_active_fbps = gp100_gr_init_rop_active_fbps, + .init_swdx_pes_mask = gp102_gr_init_swdx_pes_mask, + .init_fecs_exceptions = gp100_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_504430 = gm107_gr_init_504430, + .init_shader_exceptions = gp100_gr_init_shader_exceptions, + .trap_mp = gf100_gr_trap_mp, + .rops = gm200_gr_rops, + .gpc_nr = 2, + .tpc_nr = 3, + .ppc_nr = 1, + .grctx = &gp107_grctx, + .zbc = &gp102_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, PASCAL_B, &gf100_fermi }, + { -1, -1, PASCAL_COMPUTE_B }, + {} + } +}; + +MODULE_FIRMWARE("nvidia/gp107/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gp107/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +gp107_gr_fwif[] = { + { 0, gm200_gr_load, &gp107_gr, &gm200_gr_fecs_acr, &gm200_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gp107_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gp107_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp108.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp108.c new file mode 100644 index 000000000..7310f0466 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp108.c @@ -0,0 +1,98 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" + +#include + +#include + +static void +gp108_gr_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust) +{ + struct flcn_bl_dmem_desc_v2 hdr; + nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr)); + hdr.code_dma_base = hdr.code_dma_base + adjust; + hdr.data_dma_base = hdr.data_dma_base + adjust; + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); + flcn_bl_dmem_desc_v2_dump(&acr->subdev, &hdr); +} + +static void +gp108_gr_acr_bld_write(struct nvkm_acr *acr, u32 bld, + struct nvkm_acr_lsfw *lsfw) +{ + const u64 base = lsfw->offset.img + lsfw->app_start_offset; + const u64 code = base + lsfw->app_resident_code_offset; + const u64 data = base + lsfw->app_resident_data_offset; + const struct flcn_bl_dmem_desc_v2 hdr = { + .ctx_dma = FALCON_DMAIDX_UCODE, + .code_dma_base = code, + .non_sec_code_off = lsfw->app_resident_code_offset, + .non_sec_code_size = lsfw->app_resident_code_size, + .code_entry_point = lsfw->app_imem_entry, + .data_dma_base = data, + .data_size = lsfw->app_resident_data_size, + }; + + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); +} + +const struct nvkm_acr_lsf_func +gp108_gr_gpccs_acr = { + .flags = NVKM_ACR_LSF_FORCE_PRIV_LOAD, + .bld_size = sizeof(struct flcn_bl_dmem_desc_v2), + .bld_write = gp108_gr_acr_bld_write, + .bld_patch = gp108_gr_acr_bld_patch, +}; + +const struct nvkm_acr_lsf_func +gp108_gr_fecs_acr = { + .bld_size = sizeof(struct flcn_bl_dmem_desc_v2), + .bld_write = gp108_gr_acr_bld_write, + .bld_patch = gp108_gr_acr_bld_patch, +}; + +MODULE_FIRMWARE("nvidia/gp108/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gp108/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +gp108_gr_fwif[] = { + { 0, gm200_gr_load, &gp107_gr, &gp108_gr_fecs_acr, &gp108_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gp108_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gp108_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp10b.c new file mode 100644 index 000000000..e13683b6e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gp10b.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "gf100.h" +#include "ctxgf100.h" + +#include + +#include + +#include + +static const struct nvkm_acr_lsf_func +gp10b_gr_gpccs_acr = { + .flags = NVKM_ACR_LSF_FORCE_PRIV_LOAD, + .bld_size = sizeof(struct flcn_bl_dmem_desc), + .bld_write = gm20b_gr_acr_bld_write, + .bld_patch = gm20b_gr_acr_bld_patch, +}; + +static const struct gf100_gr_func +gp10b_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_gpc_mmu = gm200_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_rop_active_fbps = gp100_gr_init_rop_active_fbps, + .init_fecs_exceptions = gp100_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_419cc0 = gf100_gr_init_419cc0, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_tex_hww_esr = gf100_gr_init_tex_hww_esr, + .init_504430 = gm107_gr_init_504430, + .init_shader_exceptions = gp100_gr_init_shader_exceptions, + .trap_mp = gf100_gr_trap_mp, + .rops = gm200_gr_rops, + .gpc_nr = 1, + .tpc_nr = 2, + .ppc_nr = 1, + .grctx = &gp100_grctx, + .zbc = &gp100_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, PASCAL_A, &gf100_fermi }, + { -1, -1, PASCAL_COMPUTE_A }, + {} + } +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) +MODULE_FIRMWARE("nvidia/gp10b/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gp10b/gr/sw_method_init.bin"); +#endif + +static const struct gf100_gr_fwif +gp10b_gr_fwif[] = { + { 0, gm200_gr_load, &gp10b_gr, &gm20b_gr_fecs_acr, &gp10b_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gp10b_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gp10b_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c new file mode 100644 index 000000000..1dfc65d45 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c @@ -0,0 +1,49 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include + +static const struct nvkm_gr_func +gt200_gr = { + .init = nv50_gr_init, + .intr = nv50_gr_intr, + .chan_new = nv50_gr_chan_new, + .tlb_flush = g84_gr_tlb_flush, + .units = nv50_gr_units, + .sclass = { + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT200_TESLA, &nv50_gr_object }, + {} + } +}; + +int +gt200_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv50_gr_new_(>200_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c new file mode 100644 index 000000000..fcb5ead34 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include + +static const struct nvkm_gr_func +gt215_gr = { + .init = nv50_gr_init, + .intr = nv50_gr_intr, + .chan_new = nv50_gr_chan_new, + .tlb_flush = g84_gr_tlb_flush, + .units = nv50_gr_units, + .sclass = { + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT214_TESLA, &nv50_gr_object }, + { -1, -1, GT214_COMPUTE, &nv50_gr_object }, + {} + } +}; + +int +gt215_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv50_gr_new_(>215_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gv100.c new file mode 100644 index 000000000..4d043c117 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gv100.c @@ -0,0 +1,147 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +static void +gv100_gr_trap_sm(struct gf100_gr *gr, int gpc, int tpc, int sm) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 werr = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x730 + (sm * 0x80))); + u32 gerr = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x734 + (sm * 0x80))); + const struct nvkm_enum *warp; + char glob[128]; + + nvkm_snprintbf(glob, sizeof(glob), gf100_mp_global_error, gerr); + warp = nvkm_enum_find(gf100_mp_warp_error, werr & 0xffff); + + nvkm_error(subdev, "GPC%i/TPC%i/SM%d trap: " + "global %08x [%s] warp %04x [%s]\n", + gpc, tpc, sm, gerr, glob, werr, warp ? warp->name : ""); + + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x730 + sm * 0x80), 0x00000000); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x734 + sm * 0x80), gerr); +} + +void +gv100_gr_trap_mp(struct gf100_gr *gr, int gpc, int tpc) +{ + gv100_gr_trap_sm(gr, gpc, tpc, 0); + gv100_gr_trap_sm(gr, gpc, tpc, 1); +} + +static void +gv100_gr_init_4188a4(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x4188a4, 0x03000000, 0x03000000); +} + +void +gv100_gr_init_shader_exceptions(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int sm; + for (sm = 0; sm < 0x100; sm += 0x80) { + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x728 + sm), 0x0085eb64); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x610), 0x00000001); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x72c + sm), 0x00000004); + } +} + +void +gv100_gr_init_504430(struct gf100_gr *gr, int gpc, int tpc) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x430), 0x403f0000); +} + +void +gv100_gr_init_419bd8(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + nvkm_mask(device, 0x419bd8, 0x00000700, 0x00000000); +} + +static const struct gf100_gr_func +gv100_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_419bd8 = gv100_gr_init_419bd8, + .init_gpc_mmu = gm200_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = gf117_gr_init_zcull, + .init_num_active_ltcs = gm200_gr_init_num_active_ltcs, + .init_rop_active_fbps = gp100_gr_init_rop_active_fbps, + .init_swdx_pes_mask = gp102_gr_init_swdx_pes_mask, + .init_fecs_exceptions = gp100_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_504430 = gv100_gr_init_504430, + .init_shader_exceptions = gv100_gr_init_shader_exceptions, + .init_4188a4 = gv100_gr_init_4188a4, + .trap_mp = gv100_gr_trap_mp, + .rops = gm200_gr_rops, + .gpc_nr = 6, + .tpc_nr = 5, + .ppc_nr = 3, + .grctx = &gv100_grctx, + .zbc = &gp102_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, VOLTA_A, &gf100_fermi }, + { -1, -1, VOLTA_COMPUTE_A }, + {} + } +}; + +MODULE_FIRMWARE("nvidia/gv100/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/gv100/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +gv100_gr_fwif[] = { + { 0, gm200_gr_load, &gv100_gr, &gp108_gr_fecs_acr, &gp108_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +gv100_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(gv100_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c new file mode 100644 index 000000000..cf782b64f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c @@ -0,0 +1,48 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include + +static const struct nvkm_gr_func +mcp79_gr = { + .init = nv50_gr_init, + .intr = nv50_gr_intr, + .chan_new = nv50_gr_chan_new, + .units = nv50_gr_units, + .sclass = { + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT200_TESLA, &nv50_gr_object }, + {} + } +}; + +int +mcp79_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv50_gr_new_(&mcp79_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c new file mode 100644 index 000000000..6f90a6395 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include + +static const struct nvkm_gr_func +mcp89_gr = { + .init = nv50_gr_init, + .intr = nv50_gr_intr, + .chan_new = nv50_gr_chan_new, + .tlb_flush = g84_gr_tlb_flush, + .units = nv50_gr_units, + .sclass = { + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT214_COMPUTE, &nv50_gr_object }, + { -1, -1, GT21A_TESLA, &nv50_gr_object }, + {} + } +}; + +int +mcp89_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv50_gr_new_(&mcp89_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c new file mode 100644 index 000000000..0bc1a238d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c @@ -0,0 +1,1426 @@ +/* + * Copyright 2007 Stephane Marchesin + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragr) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "regs.h" + +#include +#include +#include +#include +#include +#include + +static u32 +nv04_gr_ctx_regs[] = { + 0x0040053c, + 0x00400544, + 0x00400540, + 0x00400548, + NV04_PGRAPH_CTX_SWITCH1, + NV04_PGRAPH_CTX_SWITCH2, + NV04_PGRAPH_CTX_SWITCH3, + NV04_PGRAPH_CTX_SWITCH4, + NV04_PGRAPH_CTX_CACHE1, + NV04_PGRAPH_CTX_CACHE2, + NV04_PGRAPH_CTX_CACHE3, + NV04_PGRAPH_CTX_CACHE4, + 0x00400184, + 0x004001a4, + 0x004001c4, + 0x004001e4, + 0x00400188, + 0x004001a8, + 0x004001c8, + 0x004001e8, + 0x0040018c, + 0x004001ac, + 0x004001cc, + 0x004001ec, + 0x00400190, + 0x004001b0, + 0x004001d0, + 0x004001f0, + 0x00400194, + 0x004001b4, + 0x004001d4, + 0x004001f4, + 0x00400198, + 0x004001b8, + 0x004001d8, + 0x004001f8, + 0x0040019c, + 0x004001bc, + 0x004001dc, + 0x004001fc, + 0x00400174, + NV04_PGRAPH_DMA_START_0, + NV04_PGRAPH_DMA_START_1, + NV04_PGRAPH_DMA_LENGTH, + NV04_PGRAPH_DMA_MISC, + NV04_PGRAPH_DMA_PITCH, + NV04_PGRAPH_BOFFSET0, + NV04_PGRAPH_BBASE0, + NV04_PGRAPH_BLIMIT0, + NV04_PGRAPH_BOFFSET1, + NV04_PGRAPH_BBASE1, + NV04_PGRAPH_BLIMIT1, + NV04_PGRAPH_BOFFSET2, + NV04_PGRAPH_BBASE2, + NV04_PGRAPH_BLIMIT2, + NV04_PGRAPH_BOFFSET3, + NV04_PGRAPH_BBASE3, + NV04_PGRAPH_BLIMIT3, + NV04_PGRAPH_BOFFSET4, + NV04_PGRAPH_BBASE4, + NV04_PGRAPH_BLIMIT4, + NV04_PGRAPH_BOFFSET5, + NV04_PGRAPH_BBASE5, + NV04_PGRAPH_BLIMIT5, + NV04_PGRAPH_BPITCH0, + NV04_PGRAPH_BPITCH1, + NV04_PGRAPH_BPITCH2, + NV04_PGRAPH_BPITCH3, + NV04_PGRAPH_BPITCH4, + NV04_PGRAPH_SURFACE, + NV04_PGRAPH_STATE, + NV04_PGRAPH_BSWIZZLE2, + NV04_PGRAPH_BSWIZZLE5, + NV04_PGRAPH_BPIXEL, + NV04_PGRAPH_NOTIFY, + NV04_PGRAPH_PATT_COLOR0, + NV04_PGRAPH_PATT_COLOR1, + NV04_PGRAPH_PATT_COLORRAM+0x00, + NV04_PGRAPH_PATT_COLORRAM+0x04, + NV04_PGRAPH_PATT_COLORRAM+0x08, + NV04_PGRAPH_PATT_COLORRAM+0x0c, + NV04_PGRAPH_PATT_COLORRAM+0x10, + NV04_PGRAPH_PATT_COLORRAM+0x14, + NV04_PGRAPH_PATT_COLORRAM+0x18, + NV04_PGRAPH_PATT_COLORRAM+0x1c, + NV04_PGRAPH_PATT_COLORRAM+0x20, + NV04_PGRAPH_PATT_COLORRAM+0x24, + NV04_PGRAPH_PATT_COLORRAM+0x28, + NV04_PGRAPH_PATT_COLORRAM+0x2c, + NV04_PGRAPH_PATT_COLORRAM+0x30, + NV04_PGRAPH_PATT_COLORRAM+0x34, + NV04_PGRAPH_PATT_COLORRAM+0x38, + NV04_PGRAPH_PATT_COLORRAM+0x3c, + NV04_PGRAPH_PATT_COLORRAM+0x40, + NV04_PGRAPH_PATT_COLORRAM+0x44, + NV04_PGRAPH_PATT_COLORRAM+0x48, + NV04_PGRAPH_PATT_COLORRAM+0x4c, + NV04_PGRAPH_PATT_COLORRAM+0x50, + NV04_PGRAPH_PATT_COLORRAM+0x54, + NV04_PGRAPH_PATT_COLORRAM+0x58, + NV04_PGRAPH_PATT_COLORRAM+0x5c, + NV04_PGRAPH_PATT_COLORRAM+0x60, + NV04_PGRAPH_PATT_COLORRAM+0x64, + NV04_PGRAPH_PATT_COLORRAM+0x68, + NV04_PGRAPH_PATT_COLORRAM+0x6c, + NV04_PGRAPH_PATT_COLORRAM+0x70, + NV04_PGRAPH_PATT_COLORRAM+0x74, + NV04_PGRAPH_PATT_COLORRAM+0x78, + NV04_PGRAPH_PATT_COLORRAM+0x7c, + NV04_PGRAPH_PATT_COLORRAM+0x80, + NV04_PGRAPH_PATT_COLORRAM+0x84, + NV04_PGRAPH_PATT_COLORRAM+0x88, + NV04_PGRAPH_PATT_COLORRAM+0x8c, + NV04_PGRAPH_PATT_COLORRAM+0x90, + NV04_PGRAPH_PATT_COLORRAM+0x94, + NV04_PGRAPH_PATT_COLORRAM+0x98, + NV04_PGRAPH_PATT_COLORRAM+0x9c, + NV04_PGRAPH_PATT_COLORRAM+0xa0, + NV04_PGRAPH_PATT_COLORRAM+0xa4, + NV04_PGRAPH_PATT_COLORRAM+0xa8, + NV04_PGRAPH_PATT_COLORRAM+0xac, + NV04_PGRAPH_PATT_COLORRAM+0xb0, + NV04_PGRAPH_PATT_COLORRAM+0xb4, + NV04_PGRAPH_PATT_COLORRAM+0xb8, + NV04_PGRAPH_PATT_COLORRAM+0xbc, + NV04_PGRAPH_PATT_COLORRAM+0xc0, + NV04_PGRAPH_PATT_COLORRAM+0xc4, + NV04_PGRAPH_PATT_COLORRAM+0xc8, + NV04_PGRAPH_PATT_COLORRAM+0xcc, + NV04_PGRAPH_PATT_COLORRAM+0xd0, + NV04_PGRAPH_PATT_COLORRAM+0xd4, + NV04_PGRAPH_PATT_COLORRAM+0xd8, + NV04_PGRAPH_PATT_COLORRAM+0xdc, + NV04_PGRAPH_PATT_COLORRAM+0xe0, + NV04_PGRAPH_PATT_COLORRAM+0xe4, + NV04_PGRAPH_PATT_COLORRAM+0xe8, + NV04_PGRAPH_PATT_COLORRAM+0xec, + NV04_PGRAPH_PATT_COLORRAM+0xf0, + NV04_PGRAPH_PATT_COLORRAM+0xf4, + NV04_PGRAPH_PATT_COLORRAM+0xf8, + NV04_PGRAPH_PATT_COLORRAM+0xfc, + NV04_PGRAPH_PATTERN, + 0x0040080c, + NV04_PGRAPH_PATTERN_SHAPE, + 0x00400600, + NV04_PGRAPH_ROP3, + NV04_PGRAPH_CHROMA, + NV04_PGRAPH_BETA_AND, + NV04_PGRAPH_BETA_PREMULT, + NV04_PGRAPH_CONTROL0, + NV04_PGRAPH_CONTROL1, + NV04_PGRAPH_CONTROL2, + NV04_PGRAPH_BLEND, + NV04_PGRAPH_STORED_FMT, + NV04_PGRAPH_SOURCE_COLOR, + 0x00400560, + 0x00400568, + 0x00400564, + 0x0040056c, + 0x00400400, + 0x00400480, + 0x00400404, + 0x00400484, + 0x00400408, + 0x00400488, + 0x0040040c, + 0x0040048c, + 0x00400410, + 0x00400490, + 0x00400414, + 0x00400494, + 0x00400418, + 0x00400498, + 0x0040041c, + 0x0040049c, + 0x00400420, + 0x004004a0, + 0x00400424, + 0x004004a4, + 0x00400428, + 0x004004a8, + 0x0040042c, + 0x004004ac, + 0x00400430, + 0x004004b0, + 0x00400434, + 0x004004b4, + 0x00400438, + 0x004004b8, + 0x0040043c, + 0x004004bc, + 0x00400440, + 0x004004c0, + 0x00400444, + 0x004004c4, + 0x00400448, + 0x004004c8, + 0x0040044c, + 0x004004cc, + 0x00400450, + 0x004004d0, + 0x00400454, + 0x004004d4, + 0x00400458, + 0x004004d8, + 0x0040045c, + 0x004004dc, + 0x00400460, + 0x004004e0, + 0x00400464, + 0x004004e4, + 0x00400468, + 0x004004e8, + 0x0040046c, + 0x004004ec, + 0x00400470, + 0x004004f0, + 0x00400474, + 0x004004f4, + 0x00400478, + 0x004004f8, + 0x0040047c, + 0x004004fc, + 0x00400534, + 0x00400538, + 0x00400514, + 0x00400518, + 0x0040051c, + 0x00400520, + 0x00400524, + 0x00400528, + 0x0040052c, + 0x00400530, + 0x00400d00, + 0x00400d40, + 0x00400d80, + 0x00400d04, + 0x00400d44, + 0x00400d84, + 0x00400d08, + 0x00400d48, + 0x00400d88, + 0x00400d0c, + 0x00400d4c, + 0x00400d8c, + 0x00400d10, + 0x00400d50, + 0x00400d90, + 0x00400d14, + 0x00400d54, + 0x00400d94, + 0x00400d18, + 0x00400d58, + 0x00400d98, + 0x00400d1c, + 0x00400d5c, + 0x00400d9c, + 0x00400d20, + 0x00400d60, + 0x00400da0, + 0x00400d24, + 0x00400d64, + 0x00400da4, + 0x00400d28, + 0x00400d68, + 0x00400da8, + 0x00400d2c, + 0x00400d6c, + 0x00400dac, + 0x00400d30, + 0x00400d70, + 0x00400db0, + 0x00400d34, + 0x00400d74, + 0x00400db4, + 0x00400d38, + 0x00400d78, + 0x00400db8, + 0x00400d3c, + 0x00400d7c, + 0x00400dbc, + 0x00400590, + 0x00400594, + 0x00400598, + 0x0040059c, + 0x004005a8, + 0x004005ac, + 0x004005b0, + 0x004005b4, + 0x004005c0, + 0x004005c4, + 0x004005c8, + 0x004005cc, + 0x004005d0, + 0x004005d4, + 0x004005d8, + 0x004005dc, + 0x004005e0, + NV04_PGRAPH_PASSTHRU_0, + NV04_PGRAPH_PASSTHRU_1, + NV04_PGRAPH_PASSTHRU_2, + NV04_PGRAPH_DVD_COLORFMT, + NV04_PGRAPH_SCALED_FORMAT, + NV04_PGRAPH_MISC24_0, + NV04_PGRAPH_MISC24_1, + NV04_PGRAPH_MISC24_2, + 0x00400500, + 0x00400504, + NV04_PGRAPH_VALID1, + NV04_PGRAPH_VALID2, + NV04_PGRAPH_DEBUG_3 +}; + +#define nv04_gr(p) container_of((p), struct nv04_gr, base) + +struct nv04_gr { + struct nvkm_gr base; + struct nv04_gr_chan *chan[16]; + spinlock_t lock; +}; + +#define nv04_gr_chan(p) container_of((p), struct nv04_gr_chan, object) + +struct nv04_gr_chan { + struct nvkm_object object; + struct nv04_gr *gr; + int chid; + u32 nv04[ARRAY_SIZE(nv04_gr_ctx_regs)]; +}; + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +/* + * Software methods, why they are needed, and how they all work: + * + * NV04 and NV05 keep most of the state in PGRAPH context itself, but some + * 2d engine settings are kept inside the grobjs themselves. The grobjs are + * 3 words long on both. grobj format on NV04 is: + * + * word 0: + * - bits 0-7: class + * - bit 12: color key active + * - bit 13: clip rect active + * - bit 14: if set, destination surface is swizzled and taken from buffer 5 + * [set by NV04_SWIZZLED_SURFACE], otherwise it's linear and taken + * from buffer 0 [set by NV04_CONTEXT_SURFACES_2D or + * NV03_CONTEXT_SURFACE_DST]. + * - bits 15-17: 2d operation [aka patch config] + * - bit 24: patch valid [enables rendering using this object] + * - bit 25: surf3d valid [for tex_tri and multitex_tri only] + * word 1: + * - bits 0-1: mono format + * - bits 8-13: color format + * - bits 16-31: DMA_NOTIFY instance + * word 2: + * - bits 0-15: DMA_A instance + * - bits 16-31: DMA_B instance + * + * On NV05 it's: + * + * word 0: + * - bits 0-7: class + * - bit 12: color key active + * - bit 13: clip rect active + * - bit 14: if set, destination surface is swizzled and taken from buffer 5 + * [set by NV04_SWIZZLED_SURFACE], otherwise it's linear and taken + * from buffer 0 [set by NV04_CONTEXT_SURFACES_2D or + * NV03_CONTEXT_SURFACE_DST]. + * - bits 15-17: 2d operation [aka patch config] + * - bits 20-22: dither mode + * - bit 24: patch valid [enables rendering using this object] + * - bit 25: surface_dst/surface_color/surf2d/surf3d valid + * - bit 26: surface_src/surface_zeta valid + * - bit 27: pattern valid + * - bit 28: rop valid + * - bit 29: beta1 valid + * - bit 30: beta4 valid + * word 1: + * - bits 0-1: mono format + * - bits 8-13: color format + * - bits 16-31: DMA_NOTIFY instance + * word 2: + * - bits 0-15: DMA_A instance + * - bits 16-31: DMA_B instance + * + * NV05 will set/unset the relevant valid bits when you poke the relevant + * object-binding methods with object of the proper type, or with the NULL + * type. It'll only allow rendering using the grobj if all needed objects + * are bound. The needed set of objects depends on selected operation: for + * example rop object is needed by ROP_AND, but not by SRCCOPY_AND. + * + * NV04 doesn't have these methods implemented at all, and doesn't have the + * relevant bits in grobj. Instead, it'll allow rendering whenever bit 24 + * is set. So we have to emulate them in software, internally keeping the + * same bits as NV05 does. Since grobjs are aligned to 16 bytes on nv04, + * but the last word isn't actually used for anything, we abuse it for this + * purpose. + * + * Actually, NV05 can optionally check bit 24 too, but we disable this since + * there's no use for it. + * + * For unknown reasons, NV04 implements surf3d binding in hardware as an + * exception. Also for unknown reasons, NV04 doesn't implement the clipping + * methods on the surf3d object, so we have to emulate them too. + */ + +static void +nv04_gr_set_ctx1(struct nvkm_device *device, u32 inst, u32 mask, u32 value) +{ + int subc = (nvkm_rd32(device, NV04_PGRAPH_TRAPPED_ADDR) >> 13) & 0x7; + u32 tmp; + + tmp = nvkm_rd32(device, 0x700000 + inst); + tmp &= ~mask; + tmp |= value; + nvkm_wr32(device, 0x700000 + inst, tmp); + + nvkm_wr32(device, NV04_PGRAPH_CTX_SWITCH1, tmp); + nvkm_wr32(device, NV04_PGRAPH_CTX_CACHE1 + (subc << 2), tmp); +} + +static void +nv04_gr_set_ctx_val(struct nvkm_device *device, u32 inst, u32 mask, u32 value) +{ + int class, op, valid = 1; + u32 tmp, ctx1; + + ctx1 = nvkm_rd32(device, 0x700000 + inst); + class = ctx1 & 0xff; + op = (ctx1 >> 15) & 7; + + tmp = nvkm_rd32(device, 0x70000c + inst); + tmp &= ~mask; + tmp |= value; + nvkm_wr32(device, 0x70000c + inst, tmp); + + /* check for valid surf2d/surf_dst/surf_color */ + if (!(tmp & 0x02000000)) + valid = 0; + /* check for valid surf_src/surf_zeta */ + if ((class == 0x1f || class == 0x48) && !(tmp & 0x04000000)) + valid = 0; + + switch (op) { + /* SRCCOPY_AND, SRCCOPY: no extra objects required */ + case 0: + case 3: + break; + /* ROP_AND: requires pattern and rop */ + case 1: + if (!(tmp & 0x18000000)) + valid = 0; + break; + /* BLEND_AND: requires beta1 */ + case 2: + if (!(tmp & 0x20000000)) + valid = 0; + break; + /* SRCCOPY_PREMULT, BLEND_PREMULT: beta4 required */ + case 4: + case 5: + if (!(tmp & 0x40000000)) + valid = 0; + break; + } + + nv04_gr_set_ctx1(device, inst, 0x01000000, valid << 24); +} + +static bool +nv04_gr_mthd_set_operation(struct nvkm_device *device, u32 inst, u32 data) +{ + u8 class = nvkm_rd32(device, 0x700000) & 0x000000ff; + if (data > 5) + return false; + /* Old versions of the objects only accept first three operations. */ + if (data > 2 && class < 0x40) + return false; + nv04_gr_set_ctx1(device, inst, 0x00038000, data << 15); + /* changing operation changes set of objects needed for validation */ + nv04_gr_set_ctx_val(device, inst, 0, 0); + return true; +} + +static bool +nv04_gr_mthd_surf3d_clip_h(struct nvkm_device *device, u32 inst, u32 data) +{ + u32 min = data & 0xffff, max; + u32 w = data >> 16; + if (min & 0x8000) + /* too large */ + return false; + if (w & 0x8000) + /* yes, it accepts negative for some reason. */ + w |= 0xffff0000; + max = min + w; + max &= 0x3ffff; + nvkm_wr32(device, 0x40053c, min); + nvkm_wr32(device, 0x400544, max); + return true; +} + +static bool +nv04_gr_mthd_surf3d_clip_v(struct nvkm_device *device, u32 inst, u32 data) +{ + u32 min = data & 0xffff, max; + u32 w = data >> 16; + if (min & 0x8000) + /* too large */ + return false; + if (w & 0x8000) + /* yes, it accepts negative for some reason. */ + w |= 0xffff0000; + max = min + w; + max &= 0x3ffff; + nvkm_wr32(device, 0x400540, min); + nvkm_wr32(device, 0x400548, max); + return true; +} + +static u8 +nv04_gr_mthd_bind_class(struct nvkm_device *device, u32 inst) +{ + return nvkm_rd32(device, 0x700000 + (inst << 4)); +} + +static bool +nv04_gr_mthd_bind_surf2d(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx1(device, inst, 0x00004000, 0); + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0); + return true; + case 0x42: + nv04_gr_set_ctx1(device, inst, 0x00004000, 0); + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0x02000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_surf2d_swzsurf(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx1(device, inst, 0x00004000, 0); + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0); + return true; + case 0x42: + nv04_gr_set_ctx1(device, inst, 0x00004000, 0); + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0x02000000); + return true; + case 0x52: + nv04_gr_set_ctx1(device, inst, 0x00004000, 0x00004000); + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0x02000000); + return true; + } + return false; +} + +static bool +nv01_gr_mthd_bind_patt(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x08000000, 0); + return true; + case 0x18: + nv04_gr_set_ctx_val(device, inst, 0x08000000, 0x08000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_patt(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x08000000, 0); + return true; + case 0x44: + nv04_gr_set_ctx_val(device, inst, 0x08000000, 0x08000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_rop(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x10000000, 0); + return true; + case 0x43: + nv04_gr_set_ctx_val(device, inst, 0x10000000, 0x10000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_beta1(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x20000000, 0); + return true; + case 0x12: + nv04_gr_set_ctx_val(device, inst, 0x20000000, 0x20000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_beta4(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x40000000, 0); + return true; + case 0x72: + nv04_gr_set_ctx_val(device, inst, 0x40000000, 0x40000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_surf_dst(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0); + return true; + case 0x58: + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0x02000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_surf_src(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x04000000, 0); + return true; + case 0x59: + nv04_gr_set_ctx_val(device, inst, 0x04000000, 0x04000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_surf_color(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0); + return true; + case 0x5a: + nv04_gr_set_ctx_val(device, inst, 0x02000000, 0x02000000); + return true; + } + return false; +} + +static bool +nv04_gr_mthd_bind_surf_zeta(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx_val(device, inst, 0x04000000, 0); + return true; + case 0x5b: + nv04_gr_set_ctx_val(device, inst, 0x04000000, 0x04000000); + return true; + } + return false; +} + +static bool +nv01_gr_mthd_bind_clip(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx1(device, inst, 0x2000, 0); + return true; + case 0x19: + nv04_gr_set_ctx1(device, inst, 0x2000, 0x2000); + return true; + } + return false; +} + +static bool +nv01_gr_mthd_bind_chroma(struct nvkm_device *device, u32 inst, u32 data) +{ + switch (nv04_gr_mthd_bind_class(device, data)) { + case 0x30: + nv04_gr_set_ctx1(device, inst, 0x1000, 0); + return true; + /* Yes, for some reason even the old versions of objects + * accept 0x57 and not 0x17. Consistency be damned. + */ + case 0x57: + nv04_gr_set_ctx1(device, inst, 0x1000, 0x1000); + return true; + } + return false; +} + +static bool +nv03_gr_mthd_gdi(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_patt; break; + case 0x0188: func = nv04_gr_mthd_bind_rop; break; + case 0x018c: func = nv04_gr_mthd_bind_beta1; break; + case 0x0190: func = nv04_gr_mthd_bind_surf_dst; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_gdi(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0188: func = nv04_gr_mthd_bind_patt; break; + case 0x018c: func = nv04_gr_mthd_bind_rop; break; + case 0x0190: func = nv04_gr_mthd_bind_beta1; break; + case 0x0194: func = nv04_gr_mthd_bind_beta4; break; + case 0x0198: func = nv04_gr_mthd_bind_surf2d; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv01_gr_mthd_blit(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_chroma; break; + case 0x0188: func = nv01_gr_mthd_bind_clip; break; + case 0x018c: func = nv01_gr_mthd_bind_patt; break; + case 0x0190: func = nv04_gr_mthd_bind_rop; break; + case 0x0194: func = nv04_gr_mthd_bind_beta1; break; + case 0x0198: func = nv04_gr_mthd_bind_surf_dst; break; + case 0x019c: func = nv04_gr_mthd_bind_surf_src; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_blit(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_chroma; break; + case 0x0188: func = nv01_gr_mthd_bind_clip; break; + case 0x018c: func = nv04_gr_mthd_bind_patt; break; + case 0x0190: func = nv04_gr_mthd_bind_rop; break; + case 0x0194: func = nv04_gr_mthd_bind_beta1; break; + case 0x0198: func = nv04_gr_mthd_bind_beta4; break; + case 0x019c: func = nv04_gr_mthd_bind_surf2d; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_iifc(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0188: func = nv01_gr_mthd_bind_chroma; break; + case 0x018c: func = nv01_gr_mthd_bind_clip; break; + case 0x0190: func = nv04_gr_mthd_bind_patt; break; + case 0x0194: func = nv04_gr_mthd_bind_rop; break; + case 0x0198: func = nv04_gr_mthd_bind_beta1; break; + case 0x019c: func = nv04_gr_mthd_bind_beta4; break; + case 0x01a0: func = nv04_gr_mthd_bind_surf2d_swzsurf; break; + case 0x03e4: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv01_gr_mthd_ifc(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_chroma; break; + case 0x0188: func = nv01_gr_mthd_bind_clip; break; + case 0x018c: func = nv01_gr_mthd_bind_patt; break; + case 0x0190: func = nv04_gr_mthd_bind_rop; break; + case 0x0194: func = nv04_gr_mthd_bind_beta1; break; + case 0x0198: func = nv04_gr_mthd_bind_surf_dst; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_ifc(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_chroma; break; + case 0x0188: func = nv01_gr_mthd_bind_clip; break; + case 0x018c: func = nv04_gr_mthd_bind_patt; break; + case 0x0190: func = nv04_gr_mthd_bind_rop; break; + case 0x0194: func = nv04_gr_mthd_bind_beta1; break; + case 0x0198: func = nv04_gr_mthd_bind_beta4; break; + case 0x019c: func = nv04_gr_mthd_bind_surf2d; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv03_gr_mthd_sifc(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_chroma; break; + case 0x0188: func = nv01_gr_mthd_bind_patt; break; + case 0x018c: func = nv04_gr_mthd_bind_rop; break; + case 0x0190: func = nv04_gr_mthd_bind_beta1; break; + case 0x0194: func = nv04_gr_mthd_bind_surf_dst; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_sifc(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_chroma; break; + case 0x0188: func = nv04_gr_mthd_bind_patt; break; + case 0x018c: func = nv04_gr_mthd_bind_rop; break; + case 0x0190: func = nv04_gr_mthd_bind_beta1; break; + case 0x0194: func = nv04_gr_mthd_bind_beta4; break; + case 0x0198: func = nv04_gr_mthd_bind_surf2d; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv03_gr_mthd_sifm(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0188: func = nv01_gr_mthd_bind_patt; break; + case 0x018c: func = nv04_gr_mthd_bind_rop; break; + case 0x0190: func = nv04_gr_mthd_bind_beta1; break; + case 0x0194: func = nv04_gr_mthd_bind_surf_dst; break; + case 0x0304: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_sifm(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0188: func = nv04_gr_mthd_bind_patt; break; + case 0x018c: func = nv04_gr_mthd_bind_rop; break; + case 0x0190: func = nv04_gr_mthd_bind_beta1; break; + case 0x0194: func = nv04_gr_mthd_bind_beta4; break; + case 0x0198: func = nv04_gr_mthd_bind_surf2d; break; + case 0x0304: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_surf3d(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x02f8: func = nv04_gr_mthd_surf3d_clip_h; break; + case 0x02fc: func = nv04_gr_mthd_surf3d_clip_v; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv03_gr_mthd_ttri(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0188: func = nv01_gr_mthd_bind_clip; break; + case 0x018c: func = nv04_gr_mthd_bind_surf_color; break; + case 0x0190: func = nv04_gr_mthd_bind_surf_zeta; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv01_gr_mthd_prim(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_clip; break; + case 0x0188: func = nv01_gr_mthd_bind_patt; break; + case 0x018c: func = nv04_gr_mthd_bind_rop; break; + case 0x0190: func = nv04_gr_mthd_bind_beta1; break; + case 0x0194: func = nv04_gr_mthd_bind_surf_dst; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd_prim(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32); + switch (mthd) { + case 0x0184: func = nv01_gr_mthd_bind_clip; break; + case 0x0188: func = nv04_gr_mthd_bind_patt; break; + case 0x018c: func = nv04_gr_mthd_bind_rop; break; + case 0x0190: func = nv04_gr_mthd_bind_beta1; break; + case 0x0194: func = nv04_gr_mthd_bind_beta4; break; + case 0x0198: func = nv04_gr_mthd_bind_surf2d; break; + case 0x02fc: func = nv04_gr_mthd_set_operation; break; + default: + return false; + } + return func(device, inst, data); +} + +static bool +nv04_gr_mthd(struct nvkm_device *device, u32 inst, u32 mthd, u32 data) +{ + bool (*func)(struct nvkm_device *, u32, u32, u32); + switch (nvkm_rd32(device, 0x700000 + inst) & 0x000000ff) { + case 0x1c ... 0x1e: + func = nv01_gr_mthd_prim; break; + case 0x1f: func = nv01_gr_mthd_blit; break; + case 0x21: func = nv01_gr_mthd_ifc; break; + case 0x36: func = nv03_gr_mthd_sifc; break; + case 0x37: func = nv03_gr_mthd_sifm; break; + case 0x48: func = nv03_gr_mthd_ttri; break; + case 0x4a: func = nv04_gr_mthd_gdi; break; + case 0x4b: func = nv03_gr_mthd_gdi; break; + case 0x53: func = nv04_gr_mthd_surf3d; break; + case 0x5c ... 0x5e: + func = nv04_gr_mthd_prim; break; + case 0x5f: func = nv04_gr_mthd_blit; break; + case 0x60: func = nv04_gr_mthd_iifc; break; + case 0x61: func = nv04_gr_mthd_ifc; break; + case 0x76: func = nv04_gr_mthd_sifc; break; + case 0x77: func = nv04_gr_mthd_sifm; break; + default: + return false; + } + return func(device, inst, mthd, data); +} + +static int +nv04_gr_object_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + int ret = nvkm_gpuobj_new(object->engine->subdev.device, 16, align, + false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, object->oclass); +#ifdef __BIG_ENDIAN + nvkm_mo32(*pgpuobj, 0x00, 0x00080000, 0x00080000); +#endif + nvkm_wo32(*pgpuobj, 0x04, 0x00000000); + nvkm_wo32(*pgpuobj, 0x08, 0x00000000); + nvkm_wo32(*pgpuobj, 0x0c, 0x00000000); + nvkm_done(*pgpuobj); + } + return ret; +} + +const struct nvkm_object_func +nv04_gr_object = { + .bind = nv04_gr_object_bind, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static struct nv04_gr_chan * +nv04_gr_channel(struct nv04_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nv04_gr_chan *chan = NULL; + if (nvkm_rd32(device, NV04_PGRAPH_CTX_CONTROL) & 0x00010000) { + int chid = nvkm_rd32(device, NV04_PGRAPH_CTX_USER) >> 24; + if (chid < ARRAY_SIZE(gr->chan)) + chan = gr->chan[chid]; + } + return chan; +} + +static int +nv04_gr_load_context(struct nv04_gr_chan *chan, int chid) +{ + struct nvkm_device *device = chan->gr->base.engine.subdev.device; + int i; + + for (i = 0; i < ARRAY_SIZE(nv04_gr_ctx_regs); i++) + nvkm_wr32(device, nv04_gr_ctx_regs[i], chan->nv04[i]); + + nvkm_wr32(device, NV04_PGRAPH_CTX_CONTROL, 0x10010100); + nvkm_mask(device, NV04_PGRAPH_CTX_USER, 0xff000000, chid << 24); + nvkm_mask(device, NV04_PGRAPH_FFINTFC_ST2, 0xfff00000, 0x00000000); + return 0; +} + +static int +nv04_gr_unload_context(struct nv04_gr_chan *chan) +{ + struct nvkm_device *device = chan->gr->base.engine.subdev.device; + int i; + + for (i = 0; i < ARRAY_SIZE(nv04_gr_ctx_regs); i++) + chan->nv04[i] = nvkm_rd32(device, nv04_gr_ctx_regs[i]); + + nvkm_wr32(device, NV04_PGRAPH_CTX_CONTROL, 0x10000000); + nvkm_mask(device, NV04_PGRAPH_CTX_USER, 0xff000000, 0x0f000000); + return 0; +} + +static void +nv04_gr_context_switch(struct nv04_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nv04_gr_chan *prev = NULL; + struct nv04_gr_chan *next = NULL; + int chid; + + nv04_gr_idle(&gr->base); + + /* If previous context is valid, we need to save it */ + prev = nv04_gr_channel(gr); + if (prev) + nv04_gr_unload_context(prev); + + /* load context for next channel */ + chid = (nvkm_rd32(device, NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0x0f; + next = gr->chan[chid]; + if (next) + nv04_gr_load_context(next, chid); +} + +static u32 *ctx_reg(struct nv04_gr_chan *chan, u32 reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(nv04_gr_ctx_regs); i++) { + if (nv04_gr_ctx_regs[i] == reg) + return &chan->nv04[i]; + } + + return NULL; +} + +static void * +nv04_gr_chan_dtor(struct nvkm_object *object) +{ + struct nv04_gr_chan *chan = nv04_gr_chan(object); + struct nv04_gr *gr = chan->gr; + unsigned long flags; + + spin_lock_irqsave(&gr->lock, flags); + gr->chan[chan->chid] = NULL; + spin_unlock_irqrestore(&gr->lock, flags); + return chan; +} + +static int +nv04_gr_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nv04_gr_chan *chan = nv04_gr_chan(object); + struct nv04_gr *gr = chan->gr; + struct nvkm_device *device = gr->base.engine.subdev.device; + unsigned long flags; + + spin_lock_irqsave(&gr->lock, flags); + nvkm_mask(device, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + if (nv04_gr_channel(gr) == chan) + nv04_gr_unload_context(chan); + nvkm_mask(device, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&gr->lock, flags); + return 0; +} + +static const struct nvkm_object_func +nv04_gr_chan = { + .dtor = nv04_gr_chan_dtor, + .fini = nv04_gr_chan_fini, +}; + +static int +nv04_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv04_gr *gr = nv04_gr(base); + struct nv04_gr_chan *chan; + unsigned long flags; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv04_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + *ctx_reg(chan, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31; + + spin_lock_irqsave(&gr->lock, flags); + gr->chan[chan->chid] = chan; + spin_unlock_irqrestore(&gr->lock, flags); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +bool +nv04_gr_idle(struct nvkm_gr *gr) +{ + struct nvkm_subdev *subdev = &gr->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 mask = 0xffffffff; + + if (device->card_type == NV_40) + mask &= ~NV40_PGRAPH_STATUS_SYNC_STALL; + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, NV04_PGRAPH_STATUS) & mask)) + break; + ) < 0) { + nvkm_error(subdev, "idle timed out with status %08x\n", + nvkm_rd32(device, NV04_PGRAPH_STATUS)); + return false; + } + + return true; +} + +static const struct nvkm_bitfield +nv04_gr_intr_name[] = { + { NV_PGRAPH_INTR_NOTIFY, "NOTIFY" }, + {} +}; + +static const struct nvkm_bitfield +nv04_gr_nstatus[] = { + { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" }, + { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" }, + { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" }, + { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }, + {} +}; + +const struct nvkm_bitfield +nv04_gr_nsource[] = { + { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" }, + { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" }, + { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" }, + { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" }, + { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" }, + { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" }, + { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" }, + { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" }, + { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" }, + { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" }, + { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" }, + { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" }, + { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" }, + { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" }, + { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" }, + { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" }, + { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" }, + { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" }, + { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" }, + {} +}; + +static void +nv04_gr_intr(struct nvkm_gr *base) +{ + struct nv04_gr *gr = nv04_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, NV03_PGRAPH_INTR); + u32 nsource = nvkm_rd32(device, NV03_PGRAPH_NSOURCE); + u32 nstatus = nvkm_rd32(device, NV03_PGRAPH_NSTATUS); + u32 addr = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_ADDR); + u32 chid = (addr & 0x0f000000) >> 24; + u32 subc = (addr & 0x0000e000) >> 13; + u32 mthd = (addr & 0x00001ffc); + u32 data = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nvkm_rd32(device, 0x400180 + subc * 4) & 0xff; + u32 inst = (nvkm_rd32(device, 0x40016c) & 0xffff) << 4; + u32 show = stat; + char msg[128], src[128], sta[128]; + struct nv04_gr_chan *chan; + unsigned long flags; + + spin_lock_irqsave(&gr->lock, flags); + chan = gr->chan[chid]; + + if (stat & NV_PGRAPH_INTR_NOTIFY) { + if (chan && (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD)) { + if (!nv04_gr_mthd(device, inst, mthd, data)) + show &= ~NV_PGRAPH_INTR_NOTIFY; + } + } + + if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) { + nvkm_wr32(device, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); + stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + nv04_gr_context_switch(gr); + } + + nvkm_wr32(device, NV03_PGRAPH_INTR, stat); + nvkm_wr32(device, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nvkm_snprintbf(msg, sizeof(msg), nv04_gr_intr_name, show); + nvkm_snprintbf(src, sizeof(src), nv04_gr_nsource, nsource); + nvkm_snprintbf(sta, sizeof(sta), nv04_gr_nstatus, nstatus); + nvkm_error(subdev, "intr %08x [%s] nsource %08x [%s] " + "nstatus %08x [%s] ch %d [%s] subc %d " + "class %04x mthd %04x data %08x\n", + show, msg, nsource, src, nstatus, sta, chid, + chan ? chan->object.client->name : "unknown", + subc, class, mthd, data); + } + + spin_unlock_irqrestore(&gr->lock, flags); +} + +static int +nv04_gr_init(struct nvkm_gr *base) +{ + struct nv04_gr *gr = nv04_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + + /* Enable PGRAPH interrupts */ + nvkm_wr32(device, NV03_PGRAPH_INTR, 0xFFFFFFFF); + nvkm_wr32(device, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nvkm_wr32(device, NV04_PGRAPH_VALID1, 0); + nvkm_wr32(device, NV04_PGRAPH_VALID2, 0); + /*nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0x000001FF); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/ + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0x1231c000); + /*1231C000 blob, 001 haiku*/ + /*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/ + nvkm_wr32(device, NV04_PGRAPH_DEBUG_1, 0x72111100); + /*0x72111100 blob , 01 haiku*/ + /*nvkm_wr32(device, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/ + nvkm_wr32(device, NV04_PGRAPH_DEBUG_2, 0x11d5f071); + /*haiku same*/ + + /*nvkm_wr32(device, NV04_PGRAPH_DEBUG_3, 0xfad4ff31);*/ + nvkm_wr32(device, NV04_PGRAPH_DEBUG_3, 0xf0d4ff31); + /*haiku and blob 10d4*/ + + nvkm_wr32(device, NV04_PGRAPH_STATE , 0xFFFFFFFF); + nvkm_wr32(device, NV04_PGRAPH_CTX_CONTROL , 0x10000100); + nvkm_mask(device, NV04_PGRAPH_CTX_USER, 0xff000000, 0x0f000000); + + /* These don't belong here, they're part of a per-channel context */ + nvkm_wr32(device, NV04_PGRAPH_PATTERN_SHAPE, 0x00000000); + nvkm_wr32(device, NV04_PGRAPH_BETA_AND , 0xFFFFFFFF); + return 0; +} + +static const struct nvkm_gr_func +nv04_gr = { + .init = nv04_gr_init, + .intr = nv04_gr_intr, + .chan_new = nv04_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0017, &nv04_gr_object }, /* chroma */ + { -1, -1, 0x0018, &nv04_gr_object }, /* pattern (nv01) */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x001c, &nv04_gr_object }, /* line */ + { -1, -1, 0x001d, &nv04_gr_object }, /* tri */ + { -1, -1, 0x001e, &nv04_gr_object }, /* rect */ + { -1, -1, 0x001f, &nv04_gr_object }, + { -1, -1, 0x0021, &nv04_gr_object }, + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0036, &nv04_gr_object }, + { -1, -1, 0x0037, &nv04_gr_object }, + { -1, -1, 0x0038, &nv04_gr_object }, /* dvd subpicture */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0042, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* pattern */ + { -1, -1, 0x0048, &nv04_gr_object }, + { -1, -1, 0x004a, &nv04_gr_object }, + { -1, -1, 0x004b, &nv04_gr_object }, + { -1, -1, 0x0052, &nv04_gr_object }, /* swzsurf */ + { -1, -1, 0x0053, &nv04_gr_object }, + { -1, -1, 0x0054, &nv04_gr_object }, /* ttri */ + { -1, -1, 0x0055, &nv04_gr_object }, /* mtri */ + { -1, -1, 0x0057, &nv04_gr_object }, /* chroma */ + { -1, -1, 0x0058, &nv04_gr_object }, /* surf_dst */ + { -1, -1, 0x0059, &nv04_gr_object }, /* surf_src */ + { -1, -1, 0x005a, &nv04_gr_object }, /* surf_color */ + { -1, -1, 0x005b, &nv04_gr_object }, /* surf_zeta */ + { -1, -1, 0x005c, &nv04_gr_object }, /* line */ + { -1, -1, 0x005d, &nv04_gr_object }, /* tri */ + { -1, -1, 0x005e, &nv04_gr_object }, /* rect */ + { -1, -1, 0x005f, &nv04_gr_object }, + { -1, -1, 0x0060, &nv04_gr_object }, + { -1, -1, 0x0061, &nv04_gr_object }, + { -1, -1, 0x0064, &nv04_gr_object }, /* iifc (nv05) */ + { -1, -1, 0x0065, &nv04_gr_object }, /* ifc (nv05) */ + { -1, -1, 0x0066, &nv04_gr_object }, /* sifc (nv05) */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0076, &nv04_gr_object }, + { -1, -1, 0x0077, &nv04_gr_object }, + {} + } +}; + +int +nv04_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + struct nv04_gr *gr; + + if (!(gr = kzalloc(sizeof(*gr), GFP_KERNEL))) + return -ENOMEM; + spin_lock_init(&gr->lock); + *pgr = &gr->base; + + return nvkm_gr_ctor(&nv04_gr, device, type, inst, true, &gr->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c new file mode 100644 index 000000000..942450b33 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c @@ -0,0 +1,1221 @@ +/* + * Copyright 2007 Matthieu CASTET + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragr) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "nv10.h" +#include "regs.h" + +#include +#include +#include +#include +#include + +struct pipe_state { + u32 pipe_0x0000[0x040/4]; + u32 pipe_0x0040[0x010/4]; + u32 pipe_0x0200[0x0c0/4]; + u32 pipe_0x4400[0x080/4]; + u32 pipe_0x6400[0x3b0/4]; + u32 pipe_0x6800[0x2f0/4]; + u32 pipe_0x6c00[0x030/4]; + u32 pipe_0x7000[0x130/4]; + u32 pipe_0x7400[0x0c0/4]; + u32 pipe_0x7800[0x0c0/4]; +}; + +static int nv10_gr_ctx_regs[] = { + NV10_PGRAPH_CTX_SWITCH(0), + NV10_PGRAPH_CTX_SWITCH(1), + NV10_PGRAPH_CTX_SWITCH(2), + NV10_PGRAPH_CTX_SWITCH(3), + NV10_PGRAPH_CTX_SWITCH(4), + NV10_PGRAPH_CTX_CACHE(0, 0), + NV10_PGRAPH_CTX_CACHE(0, 1), + NV10_PGRAPH_CTX_CACHE(0, 2), + NV10_PGRAPH_CTX_CACHE(0, 3), + NV10_PGRAPH_CTX_CACHE(0, 4), + NV10_PGRAPH_CTX_CACHE(1, 0), + NV10_PGRAPH_CTX_CACHE(1, 1), + NV10_PGRAPH_CTX_CACHE(1, 2), + NV10_PGRAPH_CTX_CACHE(1, 3), + NV10_PGRAPH_CTX_CACHE(1, 4), + NV10_PGRAPH_CTX_CACHE(2, 0), + NV10_PGRAPH_CTX_CACHE(2, 1), + NV10_PGRAPH_CTX_CACHE(2, 2), + NV10_PGRAPH_CTX_CACHE(2, 3), + NV10_PGRAPH_CTX_CACHE(2, 4), + NV10_PGRAPH_CTX_CACHE(3, 0), + NV10_PGRAPH_CTX_CACHE(3, 1), + NV10_PGRAPH_CTX_CACHE(3, 2), + NV10_PGRAPH_CTX_CACHE(3, 3), + NV10_PGRAPH_CTX_CACHE(3, 4), + NV10_PGRAPH_CTX_CACHE(4, 0), + NV10_PGRAPH_CTX_CACHE(4, 1), + NV10_PGRAPH_CTX_CACHE(4, 2), + NV10_PGRAPH_CTX_CACHE(4, 3), + NV10_PGRAPH_CTX_CACHE(4, 4), + NV10_PGRAPH_CTX_CACHE(5, 0), + NV10_PGRAPH_CTX_CACHE(5, 1), + NV10_PGRAPH_CTX_CACHE(5, 2), + NV10_PGRAPH_CTX_CACHE(5, 3), + NV10_PGRAPH_CTX_CACHE(5, 4), + NV10_PGRAPH_CTX_CACHE(6, 0), + NV10_PGRAPH_CTX_CACHE(6, 1), + NV10_PGRAPH_CTX_CACHE(6, 2), + NV10_PGRAPH_CTX_CACHE(6, 3), + NV10_PGRAPH_CTX_CACHE(6, 4), + NV10_PGRAPH_CTX_CACHE(7, 0), + NV10_PGRAPH_CTX_CACHE(7, 1), + NV10_PGRAPH_CTX_CACHE(7, 2), + NV10_PGRAPH_CTX_CACHE(7, 3), + NV10_PGRAPH_CTX_CACHE(7, 4), + NV10_PGRAPH_CTX_USER, + NV04_PGRAPH_DMA_START_0, + NV04_PGRAPH_DMA_START_1, + NV04_PGRAPH_DMA_LENGTH, + NV04_PGRAPH_DMA_MISC, + NV10_PGRAPH_DMA_PITCH, + NV04_PGRAPH_BOFFSET0, + NV04_PGRAPH_BBASE0, + NV04_PGRAPH_BLIMIT0, + NV04_PGRAPH_BOFFSET1, + NV04_PGRAPH_BBASE1, + NV04_PGRAPH_BLIMIT1, + NV04_PGRAPH_BOFFSET2, + NV04_PGRAPH_BBASE2, + NV04_PGRAPH_BLIMIT2, + NV04_PGRAPH_BOFFSET3, + NV04_PGRAPH_BBASE3, + NV04_PGRAPH_BLIMIT3, + NV04_PGRAPH_BOFFSET4, + NV04_PGRAPH_BBASE4, + NV04_PGRAPH_BLIMIT4, + NV04_PGRAPH_BOFFSET5, + NV04_PGRAPH_BBASE5, + NV04_PGRAPH_BLIMIT5, + NV04_PGRAPH_BPITCH0, + NV04_PGRAPH_BPITCH1, + NV04_PGRAPH_BPITCH2, + NV04_PGRAPH_BPITCH3, + NV04_PGRAPH_BPITCH4, + NV10_PGRAPH_SURFACE, + NV10_PGRAPH_STATE, + NV04_PGRAPH_BSWIZZLE2, + NV04_PGRAPH_BSWIZZLE5, + NV04_PGRAPH_BPIXEL, + NV10_PGRAPH_NOTIFY, + NV04_PGRAPH_PATT_COLOR0, + NV04_PGRAPH_PATT_COLOR1, + NV04_PGRAPH_PATT_COLORRAM, /* 64 values from 0x400900 to 0x4009fc */ + 0x00400904, + 0x00400908, + 0x0040090c, + 0x00400910, + 0x00400914, + 0x00400918, + 0x0040091c, + 0x00400920, + 0x00400924, + 0x00400928, + 0x0040092c, + 0x00400930, + 0x00400934, + 0x00400938, + 0x0040093c, + 0x00400940, + 0x00400944, + 0x00400948, + 0x0040094c, + 0x00400950, + 0x00400954, + 0x00400958, + 0x0040095c, + 0x00400960, + 0x00400964, + 0x00400968, + 0x0040096c, + 0x00400970, + 0x00400974, + 0x00400978, + 0x0040097c, + 0x00400980, + 0x00400984, + 0x00400988, + 0x0040098c, + 0x00400990, + 0x00400994, + 0x00400998, + 0x0040099c, + 0x004009a0, + 0x004009a4, + 0x004009a8, + 0x004009ac, + 0x004009b0, + 0x004009b4, + 0x004009b8, + 0x004009bc, + 0x004009c0, + 0x004009c4, + 0x004009c8, + 0x004009cc, + 0x004009d0, + 0x004009d4, + 0x004009d8, + 0x004009dc, + 0x004009e0, + 0x004009e4, + 0x004009e8, + 0x004009ec, + 0x004009f0, + 0x004009f4, + 0x004009f8, + 0x004009fc, + NV04_PGRAPH_PATTERN, /* 2 values from 0x400808 to 0x40080c */ + 0x0040080c, + NV04_PGRAPH_PATTERN_SHAPE, + NV03_PGRAPH_MONO_COLOR0, + NV04_PGRAPH_ROP3, + NV04_PGRAPH_CHROMA, + NV04_PGRAPH_BETA_AND, + NV04_PGRAPH_BETA_PREMULT, + 0x00400e70, + 0x00400e74, + 0x00400e78, + 0x00400e7c, + 0x00400e80, + 0x00400e84, + 0x00400e88, + 0x00400e8c, + 0x00400ea0, + 0x00400ea4, + 0x00400ea8, + 0x00400e90, + 0x00400e94, + 0x00400e98, + 0x00400e9c, + NV10_PGRAPH_WINDOWCLIP_HORIZONTAL, /* 8 values from 0x400f00-0x400f1c */ + NV10_PGRAPH_WINDOWCLIP_VERTICAL, /* 8 values from 0x400f20-0x400f3c */ + 0x00400f04, + 0x00400f24, + 0x00400f08, + 0x00400f28, + 0x00400f0c, + 0x00400f2c, + 0x00400f10, + 0x00400f30, + 0x00400f14, + 0x00400f34, + 0x00400f18, + 0x00400f38, + 0x00400f1c, + 0x00400f3c, + NV10_PGRAPH_XFMODE0, + NV10_PGRAPH_XFMODE1, + NV10_PGRAPH_GLOBALSTATE0, + NV10_PGRAPH_GLOBALSTATE1, + NV04_PGRAPH_STORED_FMT, + NV04_PGRAPH_SOURCE_COLOR, + NV03_PGRAPH_ABS_X_RAM, /* 32 values from 0x400400 to 0x40047c */ + NV03_PGRAPH_ABS_Y_RAM, /* 32 values from 0x400480 to 0x4004fc */ + 0x00400404, + 0x00400484, + 0x00400408, + 0x00400488, + 0x0040040c, + 0x0040048c, + 0x00400410, + 0x00400490, + 0x00400414, + 0x00400494, + 0x00400418, + 0x00400498, + 0x0040041c, + 0x0040049c, + 0x00400420, + 0x004004a0, + 0x00400424, + 0x004004a4, + 0x00400428, + 0x004004a8, + 0x0040042c, + 0x004004ac, + 0x00400430, + 0x004004b0, + 0x00400434, + 0x004004b4, + 0x00400438, + 0x004004b8, + 0x0040043c, + 0x004004bc, + 0x00400440, + 0x004004c0, + 0x00400444, + 0x004004c4, + 0x00400448, + 0x004004c8, + 0x0040044c, + 0x004004cc, + 0x00400450, + 0x004004d0, + 0x00400454, + 0x004004d4, + 0x00400458, + 0x004004d8, + 0x0040045c, + 0x004004dc, + 0x00400460, + 0x004004e0, + 0x00400464, + 0x004004e4, + 0x00400468, + 0x004004e8, + 0x0040046c, + 0x004004ec, + 0x00400470, + 0x004004f0, + 0x00400474, + 0x004004f4, + 0x00400478, + 0x004004f8, + 0x0040047c, + 0x004004fc, + NV03_PGRAPH_ABS_UCLIP_XMIN, + NV03_PGRAPH_ABS_UCLIP_XMAX, + NV03_PGRAPH_ABS_UCLIP_YMIN, + NV03_PGRAPH_ABS_UCLIP_YMAX, + 0x00400550, + 0x00400558, + 0x00400554, + 0x0040055c, + NV03_PGRAPH_ABS_UCLIPA_XMIN, + NV03_PGRAPH_ABS_UCLIPA_XMAX, + NV03_PGRAPH_ABS_UCLIPA_YMIN, + NV03_PGRAPH_ABS_UCLIPA_YMAX, + NV03_PGRAPH_ABS_ICLIP_XMAX, + NV03_PGRAPH_ABS_ICLIP_YMAX, + NV03_PGRAPH_XY_LOGIC_MISC0, + NV03_PGRAPH_XY_LOGIC_MISC1, + NV03_PGRAPH_XY_LOGIC_MISC2, + NV03_PGRAPH_XY_LOGIC_MISC3, + NV03_PGRAPH_CLIPX_0, + NV03_PGRAPH_CLIPX_1, + NV03_PGRAPH_CLIPY_0, + NV03_PGRAPH_CLIPY_1, + NV10_PGRAPH_COMBINER0_IN_ALPHA, + NV10_PGRAPH_COMBINER1_IN_ALPHA, + NV10_PGRAPH_COMBINER0_IN_RGB, + NV10_PGRAPH_COMBINER1_IN_RGB, + NV10_PGRAPH_COMBINER_COLOR0, + NV10_PGRAPH_COMBINER_COLOR1, + NV10_PGRAPH_COMBINER0_OUT_ALPHA, + NV10_PGRAPH_COMBINER1_OUT_ALPHA, + NV10_PGRAPH_COMBINER0_OUT_RGB, + NV10_PGRAPH_COMBINER1_OUT_RGB, + NV10_PGRAPH_COMBINER_FINAL0, + NV10_PGRAPH_COMBINER_FINAL1, + 0x00400e00, + 0x00400e04, + 0x00400e08, + 0x00400e0c, + 0x00400e10, + 0x00400e14, + 0x00400e18, + 0x00400e1c, + 0x00400e20, + 0x00400e24, + 0x00400e28, + 0x00400e2c, + 0x00400e30, + 0x00400e34, + 0x00400e38, + 0x00400e3c, + NV04_PGRAPH_PASSTHRU_0, + NV04_PGRAPH_PASSTHRU_1, + NV04_PGRAPH_PASSTHRU_2, + NV10_PGRAPH_DIMX_TEXTURE, + NV10_PGRAPH_WDIMX_TEXTURE, + NV10_PGRAPH_DVD_COLORFMT, + NV10_PGRAPH_SCALED_FORMAT, + NV04_PGRAPH_MISC24_0, + NV04_PGRAPH_MISC24_1, + NV04_PGRAPH_MISC24_2, + NV03_PGRAPH_X_MISC, + NV03_PGRAPH_Y_MISC, + NV04_PGRAPH_VALID1, + NV04_PGRAPH_VALID2, +}; + +static int nv17_gr_ctx_regs[] = { + NV10_PGRAPH_DEBUG_4, + 0x004006b0, + 0x00400eac, + 0x00400eb0, + 0x00400eb4, + 0x00400eb8, + 0x00400ebc, + 0x00400ec0, + 0x00400ec4, + 0x00400ec8, + 0x00400ecc, + 0x00400ed0, + 0x00400ed4, + 0x00400ed8, + 0x00400edc, + 0x00400ee0, + 0x00400a00, + 0x00400a04, +}; + +#define nv10_gr(p) container_of((p), struct nv10_gr, base) + +struct nv10_gr { + struct nvkm_gr base; + struct nv10_gr_chan *chan[32]; + spinlock_t lock; +}; + +#define nv10_gr_chan(p) container_of((p), struct nv10_gr_chan, object) + +struct nv10_gr_chan { + struct nvkm_object object; + struct nv10_gr *gr; + int chid; + int nv10[ARRAY_SIZE(nv10_gr_ctx_regs)]; + int nv17[ARRAY_SIZE(nv17_gr_ctx_regs)]; + struct pipe_state pipe_state; + u32 lma_window[4]; +}; + + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +#define PIPE_SAVE(gr, state, addr) \ + do { \ + int __i; \ + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, addr); \ + for (__i = 0; __i < ARRAY_SIZE(state); __i++) \ + state[__i] = nvkm_rd32(device, NV10_PGRAPH_PIPE_DATA); \ + } while (0) + +#define PIPE_RESTORE(gr, state, addr) \ + do { \ + int __i; \ + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, addr); \ + for (__i = 0; __i < ARRAY_SIZE(state); __i++) \ + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, state[__i]); \ + } while (0) + +static void +nv17_gr_mthd_lma_window(struct nv10_gr_chan *chan, u32 mthd, u32 data) +{ + struct nvkm_device *device = chan->object.engine->subdev.device; + struct nvkm_gr *gr = &chan->gr->base; + struct pipe_state *pipe = &chan->pipe_state; + u32 pipe_0x0040[1], pipe_0x64c0[8], pipe_0x6a80[3], pipe_0x6ab0[3]; + u32 xfmode0, xfmode1; + int i; + + chan->lma_window[(mthd - 0x1638) / 4] = data; + + if (mthd != 0x1644) + return; + + nv04_gr_idle(gr); + + PIPE_SAVE(device, pipe_0x0040, 0x0040); + PIPE_SAVE(device, pipe->pipe_0x0200, 0x0200); + + PIPE_RESTORE(device, chan->lma_window, 0x6790); + + nv04_gr_idle(gr); + + xfmode0 = nvkm_rd32(device, NV10_PGRAPH_XFMODE0); + xfmode1 = nvkm_rd32(device, NV10_PGRAPH_XFMODE1); + + PIPE_SAVE(device, pipe->pipe_0x4400, 0x4400); + PIPE_SAVE(device, pipe_0x64c0, 0x64c0); + PIPE_SAVE(device, pipe_0x6ab0, 0x6ab0); + PIPE_SAVE(device, pipe_0x6a80, 0x6a80); + + nv04_gr_idle(gr); + + nvkm_wr32(device, NV10_PGRAPH_XFMODE0, 0x10000000); + nvkm_wr32(device, NV10_PGRAPH_XFMODE1, 0x00000000); + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0); + for (i = 0; i < 4; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + for (i = 0; i < 4; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0); + for (i = 0; i < 3; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80); + for (i = 0; i < 3; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040); + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x00000008); + + PIPE_RESTORE(device, pipe->pipe_0x0200, 0x0200); + + nv04_gr_idle(gr); + + PIPE_RESTORE(device, pipe_0x0040, 0x0040); + + nvkm_wr32(device, NV10_PGRAPH_XFMODE0, xfmode0); + nvkm_wr32(device, NV10_PGRAPH_XFMODE1, xfmode1); + + PIPE_RESTORE(device, pipe_0x64c0, 0x64c0); + PIPE_RESTORE(device, pipe_0x6ab0, 0x6ab0); + PIPE_RESTORE(device, pipe_0x6a80, 0x6a80); + PIPE_RESTORE(device, pipe->pipe_0x4400, 0x4400); + + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x000000c0); + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nv04_gr_idle(gr); +} + +static void +nv17_gr_mthd_lma_enable(struct nv10_gr_chan *chan, u32 mthd, u32 data) +{ + struct nvkm_device *device = chan->object.engine->subdev.device; + struct nvkm_gr *gr = &chan->gr->base; + + nv04_gr_idle(gr); + + nvkm_mask(device, NV10_PGRAPH_DEBUG_4, 0x00000100, 0x00000100); + nvkm_mask(device, 0x4006b0, 0x08000000, 0x08000000); +} + +static bool +nv17_gr_mthd_celcius(struct nv10_gr_chan *chan, u32 mthd, u32 data) +{ + void (*func)(struct nv10_gr_chan *, u32, u32); + switch (mthd) { + case 0x1638 ... 0x1644: + func = nv17_gr_mthd_lma_window; break; + case 0x1658: func = nv17_gr_mthd_lma_enable; break; + default: + return false; + } + func(chan, mthd, data); + return true; +} + +static bool +nv10_gr_mthd(struct nv10_gr_chan *chan, u8 class, u32 mthd, u32 data) +{ + bool (*func)(struct nv10_gr_chan *, u32, u32); + switch (class) { + case 0x99: func = nv17_gr_mthd_celcius; break; + default: + return false; + } + return func(chan, mthd, data); +} + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static struct nv10_gr_chan * +nv10_gr_channel(struct nv10_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nv10_gr_chan *chan = NULL; + if (nvkm_rd32(device, 0x400144) & 0x00010000) { + int chid = nvkm_rd32(device, 0x400148) >> 24; + if (chid < ARRAY_SIZE(gr->chan)) + chan = gr->chan[chid]; + } + return chan; +} + +static void +nv10_gr_save_pipe(struct nv10_gr_chan *chan) +{ + struct nv10_gr *gr = chan->gr; + struct pipe_state *pipe = &chan->pipe_state; + struct nvkm_device *device = gr->base.engine.subdev.device; + + PIPE_SAVE(gr, pipe->pipe_0x4400, 0x4400); + PIPE_SAVE(gr, pipe->pipe_0x0200, 0x0200); + PIPE_SAVE(gr, pipe->pipe_0x6400, 0x6400); + PIPE_SAVE(gr, pipe->pipe_0x6800, 0x6800); + PIPE_SAVE(gr, pipe->pipe_0x6c00, 0x6c00); + PIPE_SAVE(gr, pipe->pipe_0x7000, 0x7000); + PIPE_SAVE(gr, pipe->pipe_0x7400, 0x7400); + PIPE_SAVE(gr, pipe->pipe_0x7800, 0x7800); + PIPE_SAVE(gr, pipe->pipe_0x0040, 0x0040); + PIPE_SAVE(gr, pipe->pipe_0x0000, 0x0000); +} + +static void +nv10_gr_load_pipe(struct nv10_gr_chan *chan) +{ + struct nv10_gr *gr = chan->gr; + struct pipe_state *pipe = &chan->pipe_state; + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 xfmode0, xfmode1; + int i; + + nv04_gr_idle(&gr->base); + /* XXX check haiku comments */ + xfmode0 = nvkm_rd32(device, NV10_PGRAPH_XFMODE0); + xfmode1 = nvkm_rd32(device, NV10_PGRAPH_XFMODE1); + nvkm_wr32(device, NV10_PGRAPH_XFMODE0, 0x10000000); + nvkm_wr32(device, NV10_PGRAPH_XFMODE1, 0x00000000); + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0); + for (i = 0; i < 4; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + for (i = 0; i < 4; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0); + for (i = 0; i < 3; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x3f800000); + + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80); + for (i = 0; i < 3; i++) + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x00000000); + + nvkm_wr32(device, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040); + nvkm_wr32(device, NV10_PGRAPH_PIPE_DATA, 0x00000008); + + + PIPE_RESTORE(gr, pipe->pipe_0x0200, 0x0200); + nv04_gr_idle(&gr->base); + + /* restore XFMODE */ + nvkm_wr32(device, NV10_PGRAPH_XFMODE0, xfmode0); + nvkm_wr32(device, NV10_PGRAPH_XFMODE1, xfmode1); + PIPE_RESTORE(gr, pipe->pipe_0x6400, 0x6400); + PIPE_RESTORE(gr, pipe->pipe_0x6800, 0x6800); + PIPE_RESTORE(gr, pipe->pipe_0x6c00, 0x6c00); + PIPE_RESTORE(gr, pipe->pipe_0x7000, 0x7000); + PIPE_RESTORE(gr, pipe->pipe_0x7400, 0x7400); + PIPE_RESTORE(gr, pipe->pipe_0x7800, 0x7800); + PIPE_RESTORE(gr, pipe->pipe_0x4400, 0x4400); + PIPE_RESTORE(gr, pipe->pipe_0x0000, 0x0000); + PIPE_RESTORE(gr, pipe->pipe_0x0040, 0x0040); + nv04_gr_idle(&gr->base); +} + +static void +nv10_gr_create_pipe(struct nv10_gr_chan *chan) +{ + struct nv10_gr *gr = chan->gr; + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct pipe_state *pipe_state = &chan->pipe_state; + u32 *pipe_state_addr; + int i; +#define PIPE_INIT(addr) \ + do { \ + pipe_state_addr = pipe_state->pipe_##addr; \ + } while (0) +#define PIPE_INIT_END(addr) \ + do { \ + u32 *__end_addr = pipe_state->pipe_##addr + \ + ARRAY_SIZE(pipe_state->pipe_##addr); \ + if (pipe_state_addr != __end_addr) \ + nvkm_error(subdev, "incomplete pipe init for 0x%x : %p/%p\n", \ + addr, pipe_state_addr, __end_addr); \ + } while (0) +#define NV_WRITE_PIPE_INIT(value) *(pipe_state_addr++) = value + + PIPE_INIT(0x0200); + for (i = 0; i < 48; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x0200); + + PIPE_INIT(0x6400); + for (i = 0; i < 211; i++) + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x3f800000); + NV_WRITE_PIPE_INIT(0x40000000); + NV_WRITE_PIPE_INIT(0x40000000); + NV_WRITE_PIPE_INIT(0x40000000); + NV_WRITE_PIPE_INIT(0x40000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x3f800000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x3f000000); + NV_WRITE_PIPE_INIT(0x3f000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x3f800000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x3f800000); + NV_WRITE_PIPE_INIT(0x3f800000); + NV_WRITE_PIPE_INIT(0x3f800000); + NV_WRITE_PIPE_INIT(0x3f800000); + PIPE_INIT_END(0x6400); + + PIPE_INIT(0x6800); + for (i = 0; i < 162; i++) + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x3f800000); + for (i = 0; i < 25; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x6800); + + PIPE_INIT(0x6c00); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0xbf800000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x6c00); + + PIPE_INIT(0x7000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x00000000); + NV_WRITE_PIPE_INIT(0x7149f2ca); + for (i = 0; i < 35; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x7000); + + PIPE_INIT(0x7400); + for (i = 0; i < 48; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x7400); + + PIPE_INIT(0x7800); + for (i = 0; i < 48; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x7800); + + PIPE_INIT(0x4400); + for (i = 0; i < 32; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x4400); + + PIPE_INIT(0x0000); + for (i = 0; i < 16; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x0000); + + PIPE_INIT(0x0040); + for (i = 0; i < 4; i++) + NV_WRITE_PIPE_INIT(0x00000000); + PIPE_INIT_END(0x0040); + +#undef PIPE_INIT +#undef PIPE_INIT_END +#undef NV_WRITE_PIPE_INIT +} + +static int +nv10_gr_ctx_regs_find_offset(struct nv10_gr *gr, int reg) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + int i; + for (i = 0; i < ARRAY_SIZE(nv10_gr_ctx_regs); i++) { + if (nv10_gr_ctx_regs[i] == reg) + return i; + } + nvkm_error(subdev, "unknown offset nv10_ctx_regs %d\n", reg); + return -1; +} + +static int +nv17_gr_ctx_regs_find_offset(struct nv10_gr *gr, int reg) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + int i; + for (i = 0; i < ARRAY_SIZE(nv17_gr_ctx_regs); i++) { + if (nv17_gr_ctx_regs[i] == reg) + return i; + } + nvkm_error(subdev, "unknown offset nv17_ctx_regs %d\n", reg); + return -1; +} + +static void +nv10_gr_load_dma_vtxbuf(struct nv10_gr_chan *chan, int chid, u32 inst) +{ + struct nv10_gr *gr = chan->gr; + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 st2, st2_dl, st2_dh, fifo_ptr, fifo[0x60/4]; + u32 ctx_user, ctx_switch[5]; + int i, subchan = -1; + + /* NV10TCL_DMA_VTXBUF (method 0x18c) modifies hidden state + * that cannot be restored via MMIO. Do it through the FIFO + * instead. + */ + + /* Look for a celsius object */ + for (i = 0; i < 8; i++) { + int class = nvkm_rd32(device, NV10_PGRAPH_CTX_CACHE(i, 0)) & 0xfff; + + if (class == 0x56 || class == 0x96 || class == 0x99) { + subchan = i; + break; + } + } + + if (subchan < 0 || !inst) + return; + + /* Save the current ctx object */ + ctx_user = nvkm_rd32(device, NV10_PGRAPH_CTX_USER); + for (i = 0; i < 5; i++) + ctx_switch[i] = nvkm_rd32(device, NV10_PGRAPH_CTX_SWITCH(i)); + + /* Save the FIFO state */ + st2 = nvkm_rd32(device, NV10_PGRAPH_FFINTFC_ST2); + st2_dl = nvkm_rd32(device, NV10_PGRAPH_FFINTFC_ST2_DL); + st2_dh = nvkm_rd32(device, NV10_PGRAPH_FFINTFC_ST2_DH); + fifo_ptr = nvkm_rd32(device, NV10_PGRAPH_FFINTFC_FIFO_PTR); + + for (i = 0; i < ARRAY_SIZE(fifo); i++) + fifo[i] = nvkm_rd32(device, 0x4007a0 + 4 * i); + + /* Switch to the celsius subchannel */ + for (i = 0; i < 5; i++) + nvkm_wr32(device, NV10_PGRAPH_CTX_SWITCH(i), + nvkm_rd32(device, NV10_PGRAPH_CTX_CACHE(subchan, i))); + nvkm_mask(device, NV10_PGRAPH_CTX_USER, 0xe000, subchan << 13); + + /* Inject NV10TCL_DMA_VTXBUF */ + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_FIFO_PTR, 0); + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_ST2, + 0x2c000000 | chid << 20 | subchan << 16 | 0x18c); + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_ST2_DL, inst); + nvkm_mask(device, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000); + nvkm_mask(device, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + nvkm_mask(device, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + + /* Restore the FIFO state */ + for (i = 0; i < ARRAY_SIZE(fifo); i++) + nvkm_wr32(device, 0x4007a0 + 4 * i, fifo[i]); + + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_FIFO_PTR, fifo_ptr); + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_ST2, st2); + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_ST2_DL, st2_dl); + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_ST2_DH, st2_dh); + + /* Restore the current ctx object */ + for (i = 0; i < 5; i++) + nvkm_wr32(device, NV10_PGRAPH_CTX_SWITCH(i), ctx_switch[i]); + nvkm_wr32(device, NV10_PGRAPH_CTX_USER, ctx_user); +} + +static int +nv10_gr_load_context(struct nv10_gr_chan *chan, int chid) +{ + struct nv10_gr *gr = chan->gr; + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 inst; + int i; + + for (i = 0; i < ARRAY_SIZE(nv10_gr_ctx_regs); i++) + nvkm_wr32(device, nv10_gr_ctx_regs[i], chan->nv10[i]); + + if (device->card_type >= NV_11 && device->chipset >= 0x17) { + for (i = 0; i < ARRAY_SIZE(nv17_gr_ctx_regs); i++) + nvkm_wr32(device, nv17_gr_ctx_regs[i], chan->nv17[i]); + } + + nv10_gr_load_pipe(chan); + + inst = nvkm_rd32(device, NV10_PGRAPH_GLOBALSTATE1) & 0xffff; + nv10_gr_load_dma_vtxbuf(chan, chid, inst); + + nvkm_wr32(device, NV10_PGRAPH_CTX_CONTROL, 0x10010100); + nvkm_mask(device, NV10_PGRAPH_CTX_USER, 0xff000000, chid << 24); + nvkm_mask(device, NV10_PGRAPH_FFINTFC_ST2, 0x30000000, 0x00000000); + return 0; +} + +static int +nv10_gr_unload_context(struct nv10_gr_chan *chan) +{ + struct nv10_gr *gr = chan->gr; + struct nvkm_device *device = gr->base.engine.subdev.device; + int i; + + for (i = 0; i < ARRAY_SIZE(nv10_gr_ctx_regs); i++) + chan->nv10[i] = nvkm_rd32(device, nv10_gr_ctx_regs[i]); + + if (device->card_type >= NV_11 && device->chipset >= 0x17) { + for (i = 0; i < ARRAY_SIZE(nv17_gr_ctx_regs); i++) + chan->nv17[i] = nvkm_rd32(device, nv17_gr_ctx_regs[i]); + } + + nv10_gr_save_pipe(chan); + + nvkm_wr32(device, NV10_PGRAPH_CTX_CONTROL, 0x10000000); + nvkm_mask(device, NV10_PGRAPH_CTX_USER, 0xff000000, 0x1f000000); + return 0; +} + +static void +nv10_gr_context_switch(struct nv10_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nv10_gr_chan *prev = NULL; + struct nv10_gr_chan *next = NULL; + int chid; + + nv04_gr_idle(&gr->base); + + /* If previous context is valid, we need to save it */ + prev = nv10_gr_channel(gr); + if (prev) + nv10_gr_unload_context(prev); + + /* load context for next channel */ + chid = (nvkm_rd32(device, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f; + next = gr->chan[chid]; + if (next) + nv10_gr_load_context(next, chid); +} + +static int +nv10_gr_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nv10_gr_chan *chan = nv10_gr_chan(object); + struct nv10_gr *gr = chan->gr; + struct nvkm_device *device = gr->base.engine.subdev.device; + unsigned long flags; + + spin_lock_irqsave(&gr->lock, flags); + nvkm_mask(device, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000); + if (nv10_gr_channel(gr) == chan) + nv10_gr_unload_context(chan); + nvkm_mask(device, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&gr->lock, flags); + return 0; +} + +static void * +nv10_gr_chan_dtor(struct nvkm_object *object) +{ + struct nv10_gr_chan *chan = nv10_gr_chan(object); + struct nv10_gr *gr = chan->gr; + unsigned long flags; + + spin_lock_irqsave(&gr->lock, flags); + gr->chan[chan->chid] = NULL; + spin_unlock_irqrestore(&gr->lock, flags); + return chan; +} + +static const struct nvkm_object_func +nv10_gr_chan = { + .dtor = nv10_gr_chan_dtor, + .fini = nv10_gr_chan_fini, +}; + +#define NV_WRITE_CTX(reg, val) do { \ + int offset = nv10_gr_ctx_regs_find_offset(gr, reg); \ + if (offset > 0) \ + chan->nv10[offset] = val; \ + } while (0) + +#define NV17_WRITE_CTX(reg, val) do { \ + int offset = nv17_gr_ctx_regs_find_offset(gr, reg); \ + if (offset > 0) \ + chan->nv17[offset] = val; \ + } while (0) + +int +nv10_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv10_gr *gr = nv10_gr(base); + struct nv10_gr_chan *chan; + struct nvkm_device *device = gr->base.engine.subdev.device; + unsigned long flags; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv10_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + NV_WRITE_CTX(0x00400e88, 0x08000000); + NV_WRITE_CTX(0x00400e9c, 0x4b7fffff); + NV_WRITE_CTX(NV03_PGRAPH_XY_LOGIC_MISC0, 0x0001ffff); + NV_WRITE_CTX(0x00400e10, 0x00001000); + NV_WRITE_CTX(0x00400e14, 0x00001000); + NV_WRITE_CTX(0x00400e30, 0x00080008); + NV_WRITE_CTX(0x00400e34, 0x00080008); + if (device->card_type >= NV_11 && device->chipset >= 0x17) { + /* is it really needed ??? */ + NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4, + nvkm_rd32(device, NV10_PGRAPH_DEBUG_4)); + NV17_WRITE_CTX(0x004006b0, nvkm_rd32(device, 0x004006b0)); + NV17_WRITE_CTX(0x00400eac, 0x0fff0000); + NV17_WRITE_CTX(0x00400eb0, 0x0fff0000); + NV17_WRITE_CTX(0x00400ec0, 0x00000080); + NV17_WRITE_CTX(0x00400ed0, 0x00000080); + } + NV_WRITE_CTX(NV10_PGRAPH_CTX_USER, chan->chid << 24); + + nv10_gr_create_pipe(chan); + + spin_lock_irqsave(&gr->lock, flags); + gr->chan[chan->chid] = chan; + spin_unlock_irqrestore(&gr->lock, flags); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +void +nv10_gr_tile(struct nvkm_gr *base, int i, struct nvkm_fb_tile *tile) +{ + struct nv10_gr *gr = nv10_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nvkm_fifo *fifo = device->fifo; + unsigned long flags; + + nvkm_fifo_pause(fifo, &flags); + nv04_gr_idle(&gr->base); + + nvkm_wr32(device, NV10_PGRAPH_TLIMIT(i), tile->limit); + nvkm_wr32(device, NV10_PGRAPH_TSIZE(i), tile->pitch); + nvkm_wr32(device, NV10_PGRAPH_TILE(i), tile->addr); + + nvkm_fifo_start(fifo, &flags); +} + +const struct nvkm_bitfield nv10_gr_intr_name[] = { + { NV_PGRAPH_INTR_NOTIFY, "NOTIFY" }, + { NV_PGRAPH_INTR_ERROR, "ERROR" }, + {} +}; + +const struct nvkm_bitfield nv10_gr_nstatus[] = { + { NV10_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" }, + { NV10_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" }, + { NV10_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" }, + { NV10_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }, + {} +}; + +void +nv10_gr_intr(struct nvkm_gr *base) +{ + struct nv10_gr *gr = nv10_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, NV03_PGRAPH_INTR); + u32 nsource = nvkm_rd32(device, NV03_PGRAPH_NSOURCE); + u32 nstatus = nvkm_rd32(device, NV03_PGRAPH_NSTATUS); + u32 addr = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_ADDR); + u32 chid = (addr & 0x01f00000) >> 20; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nvkm_rd32(device, 0x400160 + subc * 4) & 0xfff; + u32 show = stat; + char msg[128], src[128], sta[128]; + struct nv10_gr_chan *chan; + unsigned long flags; + + spin_lock_irqsave(&gr->lock, flags); + chan = gr->chan[chid]; + + if (stat & NV_PGRAPH_INTR_ERROR) { + if (chan && (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD)) { + if (!nv10_gr_mthd(chan, class, mthd, data)) + show &= ~NV_PGRAPH_INTR_ERROR; + } + } + + if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) { + nvkm_wr32(device, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); + stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; + nv10_gr_context_switch(gr); + } + + nvkm_wr32(device, NV03_PGRAPH_INTR, stat); + nvkm_wr32(device, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nvkm_snprintbf(msg, sizeof(msg), nv10_gr_intr_name, show); + nvkm_snprintbf(src, sizeof(src), nv04_gr_nsource, nsource); + nvkm_snprintbf(sta, sizeof(sta), nv10_gr_nstatus, nstatus); + nvkm_error(subdev, "intr %08x [%s] nsource %08x [%s] " + "nstatus %08x [%s] ch %d [%s] subc %d " + "class %04x mthd %04x data %08x\n", + show, msg, nsource, src, nstatus, sta, chid, + chan ? chan->object.client->name : "unknown", + subc, class, mthd, data); + } + + spin_unlock_irqrestore(&gr->lock, flags); +} + +int +nv10_gr_init(struct nvkm_gr *base) +{ + struct nv10_gr *gr = nv10_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nvkm_wr32(device, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0x00000000); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_1, 0x00118700); + /* nvkm_wr32(device, NV04_PGRAPH_DEBUG_2, 0x24E00810); */ /* 0x25f92ad9 */ + nvkm_wr32(device, NV04_PGRAPH_DEBUG_2, 0x25f92ad9); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_3, 0x55DE0830 | (1 << 29) | (1 << 31)); + + if (device->card_type >= NV_11 && device->chipset >= 0x17) { + nvkm_wr32(device, NV10_PGRAPH_DEBUG_4, 0x1f000000); + nvkm_wr32(device, 0x400a10, 0x03ff3fb6); + nvkm_wr32(device, 0x400838, 0x002f8684); + nvkm_wr32(device, 0x40083c, 0x00115f3f); + nvkm_wr32(device, 0x4006b0, 0x40000020); + } else { + nvkm_wr32(device, NV10_PGRAPH_DEBUG_4, 0x00000000); + } + + nvkm_wr32(device, NV10_PGRAPH_CTX_SWITCH(0), 0x00000000); + nvkm_wr32(device, NV10_PGRAPH_CTX_SWITCH(1), 0x00000000); + nvkm_wr32(device, NV10_PGRAPH_CTX_SWITCH(2), 0x00000000); + nvkm_wr32(device, NV10_PGRAPH_CTX_SWITCH(3), 0x00000000); + nvkm_wr32(device, NV10_PGRAPH_CTX_SWITCH(4), 0x00000000); + nvkm_wr32(device, NV10_PGRAPH_STATE, 0xFFFFFFFF); + + nvkm_mask(device, NV10_PGRAPH_CTX_USER, 0xff000000, 0x1f000000); + nvkm_wr32(device, NV10_PGRAPH_CTX_CONTROL, 0x10000100); + nvkm_wr32(device, NV10_PGRAPH_FFINTFC_ST2, 0x08000000); + return 0; +} + +int +nv10_gr_new_(const struct nvkm_gr_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + struct nv10_gr *gr; + + if (!(gr = kzalloc(sizeof(*gr), GFP_KERNEL))) + return -ENOMEM; + spin_lock_init(&gr->lock); + *pgr = &gr->base; + + return nvkm_gr_ctor(func, device, type, inst, true, &gr->base); +} + +static const struct nvkm_gr_func +nv10_gr = { + .init = nv10_gr_init, + .intr = nv10_gr_intr, + .tile = nv10_gr_tile, + .chan_new = nv10_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* pattern */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0052, &nv04_gr_object }, /* swzsurf */ + { -1, -1, 0x005f, &nv04_gr_object }, /* blit */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv04_gr_object }, /* blit */ + { -1, -1, 0x0093, &nv04_gr_object }, /* surf3d */ + { -1, -1, 0x0094, &nv04_gr_object }, /* ttri */ + { -1, -1, 0x0095, &nv04_gr_object }, /* mtri */ + { -1, -1, 0x0056, &nv04_gr_object }, /* celcius */ + {} + } +}; + +int +nv10_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv10_gr_new_(&nv10_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h new file mode 100644 index 000000000..5cfe927c9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV10_GR_H__ +#define __NV10_GR_H__ +#include "priv.h" + +int nv10_gr_new_(const struct nvkm_gr_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_gr **); +int nv10_gr_init(struct nvkm_gr *); +void nv10_gr_intr(struct nvkm_gr *); +void nv10_gr_tile(struct nvkm_gr *, int, struct nvkm_fb_tile *); + +int nv10_gr_chan_new(struct nvkm_gr *, struct nvkm_fifo_chan *, + const struct nvkm_oclass *, struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv15.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv15.c new file mode 100644 index 000000000..69ece259d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv15.c @@ -0,0 +1,59 @@ +/* + * Copyright 2007 Matthieu CASTET + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragr) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "nv10.h" + +static const struct nvkm_gr_func +nv15_gr = { + .init = nv10_gr_init, + .intr = nv10_gr_intr, + .tile = nv10_gr_tile, + .chan_new = nv10_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* pattern */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0052, &nv04_gr_object }, /* swzsurf */ + { -1, -1, 0x005f, &nv04_gr_object }, /* blit */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv04_gr_object }, /* blit */ + { -1, -1, 0x0093, &nv04_gr_object }, /* surf3d */ + { -1, -1, 0x0094, &nv04_gr_object }, /* ttri */ + { -1, -1, 0x0095, &nv04_gr_object }, /* mtri */ + { -1, -1, 0x0096, &nv04_gr_object }, /* celcius */ + {} + } +}; + +int +nv15_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv10_gr_new_(&nv15_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv17.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv17.c new file mode 100644 index 000000000..e39dfc7d4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv17.c @@ -0,0 +1,59 @@ +/* + * Copyright 2007 Matthieu CASTET + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragr) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "nv10.h" + +static const struct nvkm_gr_func +nv17_gr = { + .init = nv10_gr_init, + .intr = nv10_gr_intr, + .tile = nv10_gr_tile, + .chan_new = nv10_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* pattern */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0052, &nv04_gr_object }, /* swzsurf */ + { -1, -1, 0x005f, &nv04_gr_object }, /* blit */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv04_gr_object }, /* blit */ + { -1, -1, 0x0093, &nv04_gr_object }, /* surf3d */ + { -1, -1, 0x0094, &nv04_gr_object }, /* ttri */ + { -1, -1, 0x0095, &nv04_gr_object }, /* mtri */ + { -1, -1, 0x0099, &nv04_gr_object }, + {} + } +}; + +int +nv17_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv10_gr_new_(&nv17_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c new file mode 100644 index 000000000..6bff10cee --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: MIT +#include "nv20.h" +#include "regs.h" + +#include +#include +#include +#include +#include +#include + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +int +nv20_gr_chan_init(struct nvkm_object *object) +{ + struct nv20_gr_chan *chan = nv20_gr_chan(object); + struct nv20_gr *gr = chan->gr; + u32 inst = nvkm_memory_addr(chan->inst); + + nvkm_kmap(gr->ctxtab); + nvkm_wo32(gr->ctxtab, chan->chid * 4, inst >> 4); + nvkm_done(gr->ctxtab); + return 0; +} + +int +nv20_gr_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nv20_gr_chan *chan = nv20_gr_chan(object); + struct nv20_gr *gr = chan->gr; + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 inst = nvkm_memory_addr(chan->inst); + int chid = -1; + + nvkm_mask(device, 0x400720, 0x00000001, 0x00000000); + if (nvkm_rd32(device, 0x400144) & 0x00010000) + chid = (nvkm_rd32(device, 0x400148) & 0x1f000000) >> 24; + if (chan->chid == chid) { + nvkm_wr32(device, 0x400784, inst >> 4); + nvkm_wr32(device, 0x400788, 0x00000002); + nvkm_msec(device, 2000, + if (!nvkm_rd32(device, 0x400700)) + break; + ); + nvkm_wr32(device, 0x400144, 0x10000000); + nvkm_mask(device, 0x400148, 0xff000000, 0x1f000000); + } + nvkm_mask(device, 0x400720, 0x00000001, 0x00000001); + + nvkm_kmap(gr->ctxtab); + nvkm_wo32(gr->ctxtab, chan->chid * 4, 0x00000000); + nvkm_done(gr->ctxtab); + return 0; +} + +void * +nv20_gr_chan_dtor(struct nvkm_object *object) +{ + struct nv20_gr_chan *chan = nv20_gr_chan(object); + nvkm_memory_unref(&chan->inst); + return chan; +} + +static const struct nvkm_object_func +nv20_gr_chan = { + .dtor = nv20_gr_chan_dtor, + .init = nv20_gr_chan_init, + .fini = nv20_gr_chan_fini, +}; + +static int +nv20_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nv20_gr_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv20_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + ret = nvkm_memory_new(gr->base.engine.subdev.device, + NVKM_MEM_TARGET_INST, 0x37f0, 16, true, + &chan->inst); + if (ret) + return ret; + + nvkm_kmap(chan->inst); + nvkm_wo32(chan->inst, 0x0000, 0x00000001 | (chan->chid << 24)); + nvkm_wo32(chan->inst, 0x033c, 0xffff0000); + nvkm_wo32(chan->inst, 0x03a0, 0x0fff0000); + nvkm_wo32(chan->inst, 0x03a4, 0x0fff0000); + nvkm_wo32(chan->inst, 0x047c, 0x00000101); + nvkm_wo32(chan->inst, 0x0490, 0x00000111); + nvkm_wo32(chan->inst, 0x04a8, 0x44400000); + for (i = 0x04d4; i <= 0x04e0; i += 4) + nvkm_wo32(chan->inst, i, 0x00030303); + for (i = 0x04f4; i <= 0x0500; i += 4) + nvkm_wo32(chan->inst, i, 0x00080000); + for (i = 0x050c; i <= 0x0518; i += 4) + nvkm_wo32(chan->inst, i, 0x01012000); + for (i = 0x051c; i <= 0x0528; i += 4) + nvkm_wo32(chan->inst, i, 0x000105b8); + for (i = 0x052c; i <= 0x0538; i += 4) + nvkm_wo32(chan->inst, i, 0x00080008); + for (i = 0x055c; i <= 0x0598; i += 4) + nvkm_wo32(chan->inst, i, 0x07ff0000); + nvkm_wo32(chan->inst, 0x05a4, 0x4b7fffff); + nvkm_wo32(chan->inst, 0x05fc, 0x00000001); + nvkm_wo32(chan->inst, 0x0604, 0x00004000); + nvkm_wo32(chan->inst, 0x0610, 0x00000001); + nvkm_wo32(chan->inst, 0x0618, 0x00040000); + nvkm_wo32(chan->inst, 0x061c, 0x00010000); + for (i = 0x1c1c; i <= 0x248c; i += 16) { + nvkm_wo32(chan->inst, (i + 0), 0x10700ff9); + nvkm_wo32(chan->inst, (i + 4), 0x0436086c); + nvkm_wo32(chan->inst, (i + 8), 0x000c001b); + } + nvkm_wo32(chan->inst, 0x281c, 0x3f800000); + nvkm_wo32(chan->inst, 0x2830, 0x3f800000); + nvkm_wo32(chan->inst, 0x285c, 0x40000000); + nvkm_wo32(chan->inst, 0x2860, 0x3f800000); + nvkm_wo32(chan->inst, 0x2864, 0x3f000000); + nvkm_wo32(chan->inst, 0x286c, 0x40000000); + nvkm_wo32(chan->inst, 0x2870, 0x3f800000); + nvkm_wo32(chan->inst, 0x2878, 0xbf800000); + nvkm_wo32(chan->inst, 0x2880, 0xbf800000); + nvkm_wo32(chan->inst, 0x34a4, 0x000fe000); + nvkm_wo32(chan->inst, 0x3530, 0x000003f8); + nvkm_wo32(chan->inst, 0x3540, 0x002fe000); + for (i = 0x355c; i <= 0x3578; i += 4) + nvkm_wo32(chan->inst, i, 0x001c527c); + nvkm_done(chan->inst); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +void +nv20_gr_tile(struct nvkm_gr *base, int i, struct nvkm_fb_tile *tile) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nvkm_fifo *fifo = device->fifo; + unsigned long flags; + + nvkm_fifo_pause(fifo, &flags); + nv04_gr_idle(&gr->base); + + nvkm_wr32(device, NV20_PGRAPH_TLIMIT(i), tile->limit); + nvkm_wr32(device, NV20_PGRAPH_TSIZE(i), tile->pitch); + nvkm_wr32(device, NV20_PGRAPH_TILE(i), tile->addr); + + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0030 + 4 * i); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA, tile->limit); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0050 + 4 * i); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA, tile->pitch); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + 4 * i); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA, tile->addr); + + if (device->chipset != 0x34) { + nvkm_wr32(device, NV20_PGRAPH_ZCOMP(i), tile->zcomp); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00ea0090 + 4 * i); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA, tile->zcomp); + } + + nvkm_fifo_start(fifo, &flags); +} + +void +nv20_gr_intr(struct nvkm_gr *base) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fifo_chan *chan; + u32 stat = nvkm_rd32(device, NV03_PGRAPH_INTR); + u32 nsource = nvkm_rd32(device, NV03_PGRAPH_NSOURCE); + u32 nstatus = nvkm_rd32(device, NV03_PGRAPH_NSTATUS); + u32 addr = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_ADDR); + u32 chid = (addr & 0x01f00000) >> 20; + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nvkm_rd32(device, 0x400160 + subc * 4) & 0xfff; + u32 show = stat; + char msg[128], src[128], sta[128]; + unsigned long flags; + + chan = nvkm_fifo_chan_chid(device->fifo, chid, &flags); + + nvkm_wr32(device, NV03_PGRAPH_INTR, stat); + nvkm_wr32(device, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nvkm_snprintbf(msg, sizeof(msg), nv10_gr_intr_name, show); + nvkm_snprintbf(src, sizeof(src), nv04_gr_nsource, nsource); + nvkm_snprintbf(sta, sizeof(sta), nv10_gr_nstatus, nstatus); + nvkm_error(subdev, "intr %08x [%s] nsource %08x [%s] " + "nstatus %08x [%s] ch %d [%s] subc %d " + "class %04x mthd %04x data %08x\n", + show, msg, nsource, src, nstatus, sta, chid, + chan ? chan->object.client->name : "unknown", + subc, class, mthd, data); + } + + nvkm_fifo_chan_put(device->fifo, flags, &chan); +} + +int +nv20_gr_oneinit(struct nvkm_gr *base) +{ + struct nv20_gr *gr = nv20_gr(base); + return nvkm_memory_new(gr->base.engine.subdev.device, + NVKM_MEM_TARGET_INST, 32 * 4, 16, + true, &gr->ctxtab); +} + +int +nv20_gr_init(struct nvkm_gr *base) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + u32 tmp, vramsz; + int i; + + nvkm_wr32(device, NV20_PGRAPH_CHANNEL_CTX_TABLE, + nvkm_memory_addr(gr->ctxtab) >> 4); + + if (device->chipset == 0x20) { + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x003d0000); + for (i = 0; i < 15; i++) + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA, 0x00000000); + nvkm_msec(device, 2000, + if (!nvkm_rd32(device, 0x400700)) + break; + ); + } else { + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x02c80000); + for (i = 0; i < 32; i++) + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA, 0x00000000); + nvkm_msec(device, 2000, + if (!nvkm_rd32(device, 0x400700)) + break; + ); + } + + nvkm_wr32(device, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nvkm_wr32(device, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0x00000000); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_1, 0x00118700); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_3, 0xF3CE0475); /* 0x4 = auto ctx switch */ + nvkm_wr32(device, NV10_PGRAPH_DEBUG_4, 0x00000000); + nvkm_wr32(device, 0x40009C , 0x00000040); + + if (device->chipset >= 0x25) { + nvkm_wr32(device, 0x400890, 0x00a8cfff); + nvkm_wr32(device, 0x400610, 0x304B1FB6); + nvkm_wr32(device, 0x400B80, 0x1cbd3883); + nvkm_wr32(device, 0x400B84, 0x44000000); + nvkm_wr32(device, 0x400098, 0x40000080); + nvkm_wr32(device, 0x400B88, 0x000000ff); + + } else { + nvkm_wr32(device, 0x400880, 0x0008c7df); + nvkm_wr32(device, 0x400094, 0x00000005); + nvkm_wr32(device, 0x400B80, 0x45eae20e); + nvkm_wr32(device, 0x400B84, 0x24000000); + nvkm_wr32(device, 0x400098, 0x00000040); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00E00038); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , 0x00000030); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00E10038); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , 0x00000030); + } + + nvkm_wr32(device, 0x4009a0, nvkm_rd32(device, 0x100324)); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA000C); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA, nvkm_rd32(device, 0x100324)); + + nvkm_wr32(device, NV10_PGRAPH_CTX_CONTROL, 0x10000100); + nvkm_wr32(device, NV10_PGRAPH_STATE , 0xFFFFFFFF); + + tmp = nvkm_rd32(device, NV10_PGRAPH_SURFACE) & 0x0007ff00; + nvkm_wr32(device, NV10_PGRAPH_SURFACE, tmp); + tmp = nvkm_rd32(device, NV10_PGRAPH_SURFACE) | 0x00020100; + nvkm_wr32(device, NV10_PGRAPH_SURFACE, tmp); + + /* begin RAM config */ + vramsz = device->func->resource_size(device, 1) - 1; + nvkm_wr32(device, 0x4009A4, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x4009A8, nvkm_rd32(device, 0x100204)); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0000); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0004); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , nvkm_rd32(device, 0x100204)); + nvkm_wr32(device, 0x400820, 0); + nvkm_wr32(device, 0x400824, 0); + nvkm_wr32(device, 0x400864, vramsz - 1); + nvkm_wr32(device, 0x400868, vramsz - 1); + + /* interesting.. the below overwrites some of the tile setup above.. */ + nvkm_wr32(device, 0x400B20, 0x00000000); + nvkm_wr32(device, 0x400B04, 0xFFFFFFFF); + + nvkm_wr32(device, NV03_PGRAPH_ABS_UCLIP_XMIN, 0); + nvkm_wr32(device, NV03_PGRAPH_ABS_UCLIP_YMIN, 0); + nvkm_wr32(device, NV03_PGRAPH_ABS_UCLIP_XMAX, 0x7fff); + nvkm_wr32(device, NV03_PGRAPH_ABS_UCLIP_YMAX, 0x7fff); + return 0; +} + +void * +nv20_gr_dtor(struct nvkm_gr *base) +{ + struct nv20_gr *gr = nv20_gr(base); + nvkm_memory_unref(&gr->ctxtab); + return gr; +} + +int +nv20_gr_new_(const struct nvkm_gr_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + struct nv20_gr *gr; + + if (!(gr = kzalloc(sizeof(*gr), GFP_KERNEL))) + return -ENOMEM; + *pgr = &gr->base; + + return nvkm_gr_ctor(func, device, type, inst, true, &gr->base); +} + +static const struct nvkm_gr_func +nv20_gr = { + .dtor = nv20_gr_dtor, + .oneinit = nv20_gr_oneinit, + .init = nv20_gr_init, + .intr = nv20_gr_intr, + .tile = nv20_gr_tile, + .chan_new = nv20_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x0096, &nv04_gr_object }, /* celcius */ + { -1, -1, 0x0097, &nv04_gr_object }, /* kelvin */ + { -1, -1, 0x009e, &nv04_gr_object }, /* swzsurf */ + { -1, -1, 0x009f, &nv04_gr_object }, /* imageblit */ + {} + } +}; + +int +nv20_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv20_gr_new_(&nv20_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h new file mode 100644 index 000000000..c0d2be534 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV20_GR_H__ +#define __NV20_GR_H__ +#define nv20_gr(p) container_of((p), struct nv20_gr, base) +#include "priv.h" + +struct nv20_gr { + struct nvkm_gr base; + struct nvkm_memory *ctxtab; +}; + +int nv20_gr_new_(const struct nvkm_gr_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_gr **); +void *nv20_gr_dtor(struct nvkm_gr *); +int nv20_gr_oneinit(struct nvkm_gr *); +int nv20_gr_init(struct nvkm_gr *); +void nv20_gr_intr(struct nvkm_gr *); +void nv20_gr_tile(struct nvkm_gr *, int, struct nvkm_fb_tile *); + +int nv30_gr_init(struct nvkm_gr *); + +#define nv20_gr_chan(p) container_of((p), struct nv20_gr_chan, object) +#include + +struct nv20_gr_chan { + struct nvkm_object object; + struct nv20_gr *gr; + int chid; + struct nvkm_memory *inst; +}; + +void *nv20_gr_chan_dtor(struct nvkm_object *); +int nv20_gr_chan_init(struct nvkm_object *); +int nv20_gr_chan_fini(struct nvkm_object *, bool); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c new file mode 100644 index 000000000..f3a56f17d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +#include "nv20.h" +#include "regs.h" + +#include +#include +#include + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static const struct nvkm_object_func +nv25_gr_chan = { + .dtor = nv20_gr_chan_dtor, + .init = nv20_gr_chan_init, + .fini = nv20_gr_chan_fini, +}; + +static int +nv25_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nv20_gr_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv25_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + ret = nvkm_memory_new(gr->base.engine.subdev.device, + NVKM_MEM_TARGET_INST, 0x3724, 16, true, + &chan->inst); + if (ret) + return ret; + + nvkm_kmap(chan->inst); + nvkm_wo32(chan->inst, 0x0028, 0x00000001 | (chan->chid << 24)); + nvkm_wo32(chan->inst, 0x035c, 0xffff0000); + nvkm_wo32(chan->inst, 0x03c0, 0x0fff0000); + nvkm_wo32(chan->inst, 0x03c4, 0x0fff0000); + nvkm_wo32(chan->inst, 0x049c, 0x00000101); + nvkm_wo32(chan->inst, 0x04b0, 0x00000111); + nvkm_wo32(chan->inst, 0x04c8, 0x00000080); + nvkm_wo32(chan->inst, 0x04cc, 0xffff0000); + nvkm_wo32(chan->inst, 0x04d0, 0x00000001); + nvkm_wo32(chan->inst, 0x04e4, 0x44400000); + nvkm_wo32(chan->inst, 0x04fc, 0x4b800000); + for (i = 0x0510; i <= 0x051c; i += 4) + nvkm_wo32(chan->inst, i, 0x00030303); + for (i = 0x0530; i <= 0x053c; i += 4) + nvkm_wo32(chan->inst, i, 0x00080000); + for (i = 0x0548; i <= 0x0554; i += 4) + nvkm_wo32(chan->inst, i, 0x01012000); + for (i = 0x0558; i <= 0x0564; i += 4) + nvkm_wo32(chan->inst, i, 0x000105b8); + for (i = 0x0568; i <= 0x0574; i += 4) + nvkm_wo32(chan->inst, i, 0x00080008); + for (i = 0x0598; i <= 0x05d4; i += 4) + nvkm_wo32(chan->inst, i, 0x07ff0000); + nvkm_wo32(chan->inst, 0x05e0, 0x4b7fffff); + nvkm_wo32(chan->inst, 0x0620, 0x00000080); + nvkm_wo32(chan->inst, 0x0624, 0x30201000); + nvkm_wo32(chan->inst, 0x0628, 0x70605040); + nvkm_wo32(chan->inst, 0x062c, 0xb0a09080); + nvkm_wo32(chan->inst, 0x0630, 0xf0e0d0c0); + nvkm_wo32(chan->inst, 0x0664, 0x00000001); + nvkm_wo32(chan->inst, 0x066c, 0x00004000); + nvkm_wo32(chan->inst, 0x0678, 0x00000001); + nvkm_wo32(chan->inst, 0x0680, 0x00040000); + nvkm_wo32(chan->inst, 0x0684, 0x00010000); + for (i = 0x1b04; i <= 0x2374; i += 16) { + nvkm_wo32(chan->inst, (i + 0), 0x10700ff9); + nvkm_wo32(chan->inst, (i + 4), 0x0436086c); + nvkm_wo32(chan->inst, (i + 8), 0x000c001b); + } + nvkm_wo32(chan->inst, 0x2704, 0x3f800000); + nvkm_wo32(chan->inst, 0x2718, 0x3f800000); + nvkm_wo32(chan->inst, 0x2744, 0x40000000); + nvkm_wo32(chan->inst, 0x2748, 0x3f800000); + nvkm_wo32(chan->inst, 0x274c, 0x3f000000); + nvkm_wo32(chan->inst, 0x2754, 0x40000000); + nvkm_wo32(chan->inst, 0x2758, 0x3f800000); + nvkm_wo32(chan->inst, 0x2760, 0xbf800000); + nvkm_wo32(chan->inst, 0x2768, 0xbf800000); + nvkm_wo32(chan->inst, 0x308c, 0x000fe000); + nvkm_wo32(chan->inst, 0x3108, 0x000003f8); + nvkm_wo32(chan->inst, 0x3468, 0x002fe000); + for (i = 0x3484; i <= 0x34a0; i += 4) + nvkm_wo32(chan->inst, i, 0x001c527c); + nvkm_done(chan->inst); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_gr_func +nv25_gr = { + .dtor = nv20_gr_dtor, + .oneinit = nv20_gr_oneinit, + .init = nv20_gr_init, + .intr = nv20_gr_intr, + .tile = nv20_gr_tile, + .chan_new = nv25_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x0096, &nv04_gr_object }, /* celcius */ + { -1, -1, 0x009e, &nv04_gr_object }, /* swzsurf */ + { -1, -1, 0x009f, &nv04_gr_object }, /* imageblit */ + { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */ + {} + } +}; + +int +nv25_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv20_gr_new_(&nv25_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c new file mode 100644 index 000000000..f268d2642 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT +#include "nv20.h" +#include "regs.h" + +#include +#include +#include + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static const struct nvkm_object_func +nv2a_gr_chan = { + .dtor = nv20_gr_chan_dtor, + .init = nv20_gr_chan_init, + .fini = nv20_gr_chan_fini, +}; + +static int +nv2a_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nv20_gr_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv2a_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + ret = nvkm_memory_new(gr->base.engine.subdev.device, + NVKM_MEM_TARGET_INST, 0x36b0, 16, true, + &chan->inst); + if (ret) + return ret; + + nvkm_kmap(chan->inst); + nvkm_wo32(chan->inst, 0x0000, 0x00000001 | (chan->chid << 24)); + nvkm_wo32(chan->inst, 0x033c, 0xffff0000); + nvkm_wo32(chan->inst, 0x03a0, 0x0fff0000); + nvkm_wo32(chan->inst, 0x03a4, 0x0fff0000); + nvkm_wo32(chan->inst, 0x047c, 0x00000101); + nvkm_wo32(chan->inst, 0x0490, 0x00000111); + nvkm_wo32(chan->inst, 0x04a8, 0x44400000); + for (i = 0x04d4; i <= 0x04e0; i += 4) + nvkm_wo32(chan->inst, i, 0x00030303); + for (i = 0x04f4; i <= 0x0500; i += 4) + nvkm_wo32(chan->inst, i, 0x00080000); + for (i = 0x050c; i <= 0x0518; i += 4) + nvkm_wo32(chan->inst, i, 0x01012000); + for (i = 0x051c; i <= 0x0528; i += 4) + nvkm_wo32(chan->inst, i, 0x000105b8); + for (i = 0x052c; i <= 0x0538; i += 4) + nvkm_wo32(chan->inst, i, 0x00080008); + for (i = 0x055c; i <= 0x0598; i += 4) + nvkm_wo32(chan->inst, i, 0x07ff0000); + nvkm_wo32(chan->inst, 0x05a4, 0x4b7fffff); + nvkm_wo32(chan->inst, 0x05fc, 0x00000001); + nvkm_wo32(chan->inst, 0x0604, 0x00004000); + nvkm_wo32(chan->inst, 0x0610, 0x00000001); + nvkm_wo32(chan->inst, 0x0618, 0x00040000); + nvkm_wo32(chan->inst, 0x061c, 0x00010000); + for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */ + nvkm_wo32(chan->inst, (i + 0), 0x10700ff9); + nvkm_wo32(chan->inst, (i + 4), 0x0436086c); + nvkm_wo32(chan->inst, (i + 8), 0x000c001b); + } + nvkm_wo32(chan->inst, 0x269c, 0x3f800000); + nvkm_wo32(chan->inst, 0x26b0, 0x3f800000); + nvkm_wo32(chan->inst, 0x26dc, 0x40000000); + nvkm_wo32(chan->inst, 0x26e0, 0x3f800000); + nvkm_wo32(chan->inst, 0x26e4, 0x3f000000); + nvkm_wo32(chan->inst, 0x26ec, 0x40000000); + nvkm_wo32(chan->inst, 0x26f0, 0x3f800000); + nvkm_wo32(chan->inst, 0x26f8, 0xbf800000); + nvkm_wo32(chan->inst, 0x2700, 0xbf800000); + nvkm_wo32(chan->inst, 0x3024, 0x000fe000); + nvkm_wo32(chan->inst, 0x30a0, 0x000003f8); + nvkm_wo32(chan->inst, 0x33fc, 0x002fe000); + for (i = 0x341c; i <= 0x3438; i += 4) + nvkm_wo32(chan->inst, i, 0x001c527c); + nvkm_done(chan->inst); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_gr_func +nv2a_gr = { + .dtor = nv20_gr_dtor, + .oneinit = nv20_gr_oneinit, + .init = nv20_gr_init, + .intr = nv20_gr_intr, + .tile = nv20_gr_tile, + .chan_new = nv2a_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x0096, &nv04_gr_object }, /* celcius */ + { -1, -1, 0x009e, &nv04_gr_object }, /* swzsurf */ + { -1, -1, 0x009f, &nv04_gr_object }, /* imageblit */ + { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */ + {} + } +}; + +int +nv2a_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv20_gr_new_(&nv2a_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c new file mode 100644 index 000000000..e5737cdf2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +#include "nv20.h" +#include "regs.h" + +#include +#include +#include +#include + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static const struct nvkm_object_func +nv30_gr_chan = { + .dtor = nv20_gr_chan_dtor, + .init = nv20_gr_chan_init, + .fini = nv20_gr_chan_fini, +}; + +static int +nv30_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nv20_gr_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv30_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + ret = nvkm_memory_new(gr->base.engine.subdev.device, + NVKM_MEM_TARGET_INST, 0x5f48, 16, true, + &chan->inst); + if (ret) + return ret; + + nvkm_kmap(chan->inst); + nvkm_wo32(chan->inst, 0x0028, 0x00000001 | (chan->chid << 24)); + nvkm_wo32(chan->inst, 0x0410, 0x00000101); + nvkm_wo32(chan->inst, 0x0424, 0x00000111); + nvkm_wo32(chan->inst, 0x0428, 0x00000060); + nvkm_wo32(chan->inst, 0x0444, 0x00000080); + nvkm_wo32(chan->inst, 0x0448, 0xffff0000); + nvkm_wo32(chan->inst, 0x044c, 0x00000001); + nvkm_wo32(chan->inst, 0x0460, 0x44400000); + nvkm_wo32(chan->inst, 0x048c, 0xffff0000); + for (i = 0x04e0; i < 0x04e8; i += 4) + nvkm_wo32(chan->inst, i, 0x0fff0000); + nvkm_wo32(chan->inst, 0x04ec, 0x00011100); + for (i = 0x0508; i < 0x0548; i += 4) + nvkm_wo32(chan->inst, i, 0x07ff0000); + nvkm_wo32(chan->inst, 0x0550, 0x4b7fffff); + nvkm_wo32(chan->inst, 0x058c, 0x00000080); + nvkm_wo32(chan->inst, 0x0590, 0x30201000); + nvkm_wo32(chan->inst, 0x0594, 0x70605040); + nvkm_wo32(chan->inst, 0x0598, 0xb8a89888); + nvkm_wo32(chan->inst, 0x059c, 0xf8e8d8c8); + nvkm_wo32(chan->inst, 0x05b0, 0xb0000000); + for (i = 0x0600; i < 0x0640; i += 4) + nvkm_wo32(chan->inst, i, 0x00010588); + for (i = 0x0640; i < 0x0680; i += 4) + nvkm_wo32(chan->inst, i, 0x00030303); + for (i = 0x06c0; i < 0x0700; i += 4) + nvkm_wo32(chan->inst, i, 0x0008aae4); + for (i = 0x0700; i < 0x0740; i += 4) + nvkm_wo32(chan->inst, i, 0x01012000); + for (i = 0x0740; i < 0x0780; i += 4) + nvkm_wo32(chan->inst, i, 0x00080008); + nvkm_wo32(chan->inst, 0x085c, 0x00040000); + nvkm_wo32(chan->inst, 0x0860, 0x00010000); + for (i = 0x0864; i < 0x0874; i += 4) + nvkm_wo32(chan->inst, i, 0x00040004); + for (i = 0x1f18; i <= 0x3088 ; i += 16) { + nvkm_wo32(chan->inst, i + 0, 0x10700ff9); + nvkm_wo32(chan->inst, i + 4, 0x0436086c); + nvkm_wo32(chan->inst, i + 8, 0x000c001b); + } + for (i = 0x30b8; i < 0x30c8; i += 4) + nvkm_wo32(chan->inst, i, 0x0000ffff); + nvkm_wo32(chan->inst, 0x344c, 0x3f800000); + nvkm_wo32(chan->inst, 0x3808, 0x3f800000); + nvkm_wo32(chan->inst, 0x381c, 0x3f800000); + nvkm_wo32(chan->inst, 0x3848, 0x40000000); + nvkm_wo32(chan->inst, 0x384c, 0x3f800000); + nvkm_wo32(chan->inst, 0x3850, 0x3f000000); + nvkm_wo32(chan->inst, 0x3858, 0x40000000); + nvkm_wo32(chan->inst, 0x385c, 0x3f800000); + nvkm_wo32(chan->inst, 0x3864, 0xbf800000); + nvkm_wo32(chan->inst, 0x386c, 0xbf800000); + nvkm_done(chan->inst); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +int +nv30_gr_init(struct nvkm_gr *base) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, NV20_PGRAPH_CHANNEL_CTX_TABLE, + nvkm_memory_addr(gr->ctxtab) >> 4); + + nvkm_wr32(device, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nvkm_wr32(device, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0x00000000); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_1, 0x401287c0); + nvkm_wr32(device, 0x400890, 0x01b463ff); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_3, 0xf2de0475); + nvkm_wr32(device, NV10_PGRAPH_DEBUG_4, 0x00008000); + nvkm_wr32(device, NV04_PGRAPH_LIMIT_VIOL_PIX, 0xf04bdff6); + nvkm_wr32(device, 0x400B80, 0x1003d888); + nvkm_wr32(device, 0x400B84, 0x0c000000); + nvkm_wr32(device, 0x400098, 0x00000000); + nvkm_wr32(device, 0x40009C, 0x0005ad00); + nvkm_wr32(device, 0x400B88, 0x62ff00ff); /* suspiciously like PGRAPH_DEBUG_2 */ + nvkm_wr32(device, 0x4000a0, 0x00000000); + nvkm_wr32(device, 0x4000a4, 0x00000008); + nvkm_wr32(device, 0x4008a8, 0xb784a400); + nvkm_wr32(device, 0x400ba0, 0x002f8685); + nvkm_wr32(device, 0x400ba4, 0x00231f3f); + nvkm_wr32(device, 0x4008a4, 0x40000020); + + if (device->chipset == 0x34) { + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0004); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , 0x00200201); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0008); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , 0x00000008); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00EA0000); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , 0x00000032); + nvkm_wr32(device, NV10_PGRAPH_RDI_INDEX, 0x00E00004); + nvkm_wr32(device, NV10_PGRAPH_RDI_DATA , 0x00000002); + } + + nvkm_wr32(device, 0x4000c0, 0x00000016); + + nvkm_wr32(device, NV10_PGRAPH_CTX_CONTROL, 0x10000100); + nvkm_wr32(device, NV10_PGRAPH_STATE , 0xFFFFFFFF); + nvkm_wr32(device, 0x0040075c , 0x00000001); + + /* begin RAM config */ + /* vramsz = pci_resource_len(gr->dev->pdev, 1) - 1; */ + nvkm_wr32(device, 0x4009A4, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x4009A8, nvkm_rd32(device, 0x100204)); + if (device->chipset != 0x34) { + nvkm_wr32(device, 0x400750, 0x00EA0000); + nvkm_wr32(device, 0x400754, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x400750, 0x00EA0004); + nvkm_wr32(device, 0x400754, nvkm_rd32(device, 0x100204)); + } + + return 0; +} + +static const struct nvkm_gr_func +nv30_gr = { + .dtor = nv20_gr_dtor, + .oneinit = nv20_gr_oneinit, + .init = nv30_gr_init, + .intr = nv20_gr_intr, + .tile = nv20_gr_tile, + .chan_new = nv30_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv04_gr_object }, /* imageblit */ + { -1, -1, 0x0362, &nv04_gr_object }, /* surf2d (nv30) */ + { -1, -1, 0x0389, &nv04_gr_object }, /* sifm (nv30) */ + { -1, -1, 0x038a, &nv04_gr_object }, /* ifc (nv30) */ + { -1, -1, 0x039e, &nv04_gr_object }, /* swzsurf (nv30) */ + { -1, -1, 0x0397, &nv04_gr_object }, /* rankine */ + { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */ + {} + } +}; + +int +nv30_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv20_gr_new_(&nv30_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c new file mode 100644 index 000000000..1ab2da8eb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +#include "nv20.h" +#include "regs.h" + +#include +#include +#include + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static const struct nvkm_object_func +nv34_gr_chan = { + .dtor = nv20_gr_chan_dtor, + .init = nv20_gr_chan_init, + .fini = nv20_gr_chan_fini, +}; + +static int +nv34_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nv20_gr_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv34_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + ret = nvkm_memory_new(gr->base.engine.subdev.device, + NVKM_MEM_TARGET_INST, 0x46dc, 16, true, + &chan->inst); + if (ret) + return ret; + + nvkm_kmap(chan->inst); + nvkm_wo32(chan->inst, 0x0028, 0x00000001 | (chan->chid << 24)); + nvkm_wo32(chan->inst, 0x040c, 0x01000101); + nvkm_wo32(chan->inst, 0x0420, 0x00000111); + nvkm_wo32(chan->inst, 0x0424, 0x00000060); + nvkm_wo32(chan->inst, 0x0440, 0x00000080); + nvkm_wo32(chan->inst, 0x0444, 0xffff0000); + nvkm_wo32(chan->inst, 0x0448, 0x00000001); + nvkm_wo32(chan->inst, 0x045c, 0x44400000); + nvkm_wo32(chan->inst, 0x0480, 0xffff0000); + for (i = 0x04d4; i < 0x04dc; i += 4) + nvkm_wo32(chan->inst, i, 0x0fff0000); + nvkm_wo32(chan->inst, 0x04e0, 0x00011100); + for (i = 0x04fc; i < 0x053c; i += 4) + nvkm_wo32(chan->inst, i, 0x07ff0000); + nvkm_wo32(chan->inst, 0x0544, 0x4b7fffff); + nvkm_wo32(chan->inst, 0x057c, 0x00000080); + nvkm_wo32(chan->inst, 0x0580, 0x30201000); + nvkm_wo32(chan->inst, 0x0584, 0x70605040); + nvkm_wo32(chan->inst, 0x0588, 0xb8a89888); + nvkm_wo32(chan->inst, 0x058c, 0xf8e8d8c8); + nvkm_wo32(chan->inst, 0x05a0, 0xb0000000); + for (i = 0x05f0; i < 0x0630; i += 4) + nvkm_wo32(chan->inst, i, 0x00010588); + for (i = 0x0630; i < 0x0670; i += 4) + nvkm_wo32(chan->inst, i, 0x00030303); + for (i = 0x06b0; i < 0x06f0; i += 4) + nvkm_wo32(chan->inst, i, 0x0008aae4); + for (i = 0x06f0; i < 0x0730; i += 4) + nvkm_wo32(chan->inst, i, 0x01012000); + for (i = 0x0730; i < 0x0770; i += 4) + nvkm_wo32(chan->inst, i, 0x00080008); + nvkm_wo32(chan->inst, 0x0850, 0x00040000); + nvkm_wo32(chan->inst, 0x0854, 0x00010000); + for (i = 0x0858; i < 0x0868; i += 4) + nvkm_wo32(chan->inst, i, 0x00040004); + for (i = 0x15ac; i <= 0x271c ; i += 16) { + nvkm_wo32(chan->inst, i + 0, 0x10700ff9); + nvkm_wo32(chan->inst, i + 4, 0x0436086c); + nvkm_wo32(chan->inst, i + 8, 0x000c001b); + } + for (i = 0x274c; i < 0x275c; i += 4) + nvkm_wo32(chan->inst, i, 0x0000ffff); + nvkm_wo32(chan->inst, 0x2ae0, 0x3f800000); + nvkm_wo32(chan->inst, 0x2e9c, 0x3f800000); + nvkm_wo32(chan->inst, 0x2eb0, 0x3f800000); + nvkm_wo32(chan->inst, 0x2edc, 0x40000000); + nvkm_wo32(chan->inst, 0x2ee0, 0x3f800000); + nvkm_wo32(chan->inst, 0x2ee4, 0x3f000000); + nvkm_wo32(chan->inst, 0x2eec, 0x40000000); + nvkm_wo32(chan->inst, 0x2ef0, 0x3f800000); + nvkm_wo32(chan->inst, 0x2ef8, 0xbf800000); + nvkm_wo32(chan->inst, 0x2f00, 0xbf800000); + nvkm_done(chan->inst); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_gr_func +nv34_gr = { + .dtor = nv20_gr_dtor, + .oneinit = nv20_gr_oneinit, + .init = nv30_gr_init, + .intr = nv20_gr_intr, + .tile = nv20_gr_tile, + .chan_new = nv34_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv04_gr_object }, /* imageblit */ + { -1, -1, 0x0362, &nv04_gr_object }, /* surf2d (nv30) */ + { -1, -1, 0x0389, &nv04_gr_object }, /* sifm (nv30) */ + { -1, -1, 0x038a, &nv04_gr_object }, /* ifc (nv30) */ + { -1, -1, 0x039e, &nv04_gr_object }, /* swzsurf (nv30) */ + { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */ + { -1, -1, 0x0697, &nv04_gr_object }, /* rankine */ + {} + } +}; + +int +nv34_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv20_gr_new_(&nv34_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c new file mode 100644 index 000000000..591260f56 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +#include "nv20.h" +#include "regs.h" + +#include +#include +#include + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static const struct nvkm_object_func +nv35_gr_chan = { + .dtor = nv20_gr_chan_dtor, + .init = nv20_gr_chan_init, + .fini = nv20_gr_chan_fini, +}; + +static int +nv35_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv20_gr *gr = nv20_gr(base); + struct nv20_gr_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv35_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->chid = fifoch->chid; + *pobject = &chan->object; + + ret = nvkm_memory_new(gr->base.engine.subdev.device, + NVKM_MEM_TARGET_INST, 0x577c, 16, true, + &chan->inst); + if (ret) + return ret; + + nvkm_kmap(chan->inst); + nvkm_wo32(chan->inst, 0x0028, 0x00000001 | (chan->chid << 24)); + nvkm_wo32(chan->inst, 0x040c, 0x00000101); + nvkm_wo32(chan->inst, 0x0420, 0x00000111); + nvkm_wo32(chan->inst, 0x0424, 0x00000060); + nvkm_wo32(chan->inst, 0x0440, 0x00000080); + nvkm_wo32(chan->inst, 0x0444, 0xffff0000); + nvkm_wo32(chan->inst, 0x0448, 0x00000001); + nvkm_wo32(chan->inst, 0x045c, 0x44400000); + nvkm_wo32(chan->inst, 0x0488, 0xffff0000); + for (i = 0x04dc; i < 0x04e4; i += 4) + nvkm_wo32(chan->inst, i, 0x0fff0000); + nvkm_wo32(chan->inst, 0x04e8, 0x00011100); + for (i = 0x0504; i < 0x0544; i += 4) + nvkm_wo32(chan->inst, i, 0x07ff0000); + nvkm_wo32(chan->inst, 0x054c, 0x4b7fffff); + nvkm_wo32(chan->inst, 0x0588, 0x00000080); + nvkm_wo32(chan->inst, 0x058c, 0x30201000); + nvkm_wo32(chan->inst, 0x0590, 0x70605040); + nvkm_wo32(chan->inst, 0x0594, 0xb8a89888); + nvkm_wo32(chan->inst, 0x0598, 0xf8e8d8c8); + nvkm_wo32(chan->inst, 0x05ac, 0xb0000000); + for (i = 0x0604; i < 0x0644; i += 4) + nvkm_wo32(chan->inst, i, 0x00010588); + for (i = 0x0644; i < 0x0684; i += 4) + nvkm_wo32(chan->inst, i, 0x00030303); + for (i = 0x06c4; i < 0x0704; i += 4) + nvkm_wo32(chan->inst, i, 0x0008aae4); + for (i = 0x0704; i < 0x0744; i += 4) + nvkm_wo32(chan->inst, i, 0x01012000); + for (i = 0x0744; i < 0x0784; i += 4) + nvkm_wo32(chan->inst, i, 0x00080008); + nvkm_wo32(chan->inst, 0x0860, 0x00040000); + nvkm_wo32(chan->inst, 0x0864, 0x00010000); + for (i = 0x0868; i < 0x0878; i += 4) + nvkm_wo32(chan->inst, i, 0x00040004); + for (i = 0x1f1c; i <= 0x308c ; i += 16) { + nvkm_wo32(chan->inst, i + 0, 0x10700ff9); + nvkm_wo32(chan->inst, i + 4, 0x0436086c); + nvkm_wo32(chan->inst, i + 8, 0x000c001b); + } + for (i = 0x30bc; i < 0x30cc; i += 4) + nvkm_wo32(chan->inst, i, 0x0000ffff); + nvkm_wo32(chan->inst, 0x3450, 0x3f800000); + nvkm_wo32(chan->inst, 0x380c, 0x3f800000); + nvkm_wo32(chan->inst, 0x3820, 0x3f800000); + nvkm_wo32(chan->inst, 0x384c, 0x40000000); + nvkm_wo32(chan->inst, 0x3850, 0x3f800000); + nvkm_wo32(chan->inst, 0x3854, 0x3f000000); + nvkm_wo32(chan->inst, 0x385c, 0x40000000); + nvkm_wo32(chan->inst, 0x3860, 0x3f800000); + nvkm_wo32(chan->inst, 0x3868, 0xbf800000); + nvkm_wo32(chan->inst, 0x3870, 0xbf800000); + nvkm_done(chan->inst); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_gr_func +nv35_gr = { + .dtor = nv20_gr_dtor, + .oneinit = nv20_gr_oneinit, + .init = nv30_gr_init, + .intr = nv20_gr_intr, + .tile = nv20_gr_tile, + .chan_new = nv35_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv04_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv04_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv04_gr_object }, /* null */ + { -1, -1, 0x0039, &nv04_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv04_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv04_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv04_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv04_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv04_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv04_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv04_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv04_gr_object }, /* imageblit */ + { -1, -1, 0x0362, &nv04_gr_object }, /* surf2d (nv30) */ + { -1, -1, 0x0389, &nv04_gr_object }, /* sifm (nv30) */ + { -1, -1, 0x038a, &nv04_gr_object }, /* ifc (nv30) */ + { -1, -1, 0x039e, &nv04_gr_object }, /* swzsurf (nv30) */ + { -1, -1, 0x0497, &nv04_gr_object }, /* rankine */ + { -1, -1, 0x0597, &nv04_gr_object }, /* kelvin */ + {} + } +}; + +int +nv35_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv20_gr_new_(&nv35_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c new file mode 100644 index 000000000..67f3535ff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c @@ -0,0 +1,476 @@ +/* + * 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 "nv40.h" +#include "regs.h" + +#include +#include +#include +#include +#include + +u64 +nv40_gr_units(struct nvkm_gr *gr) +{ + return nvkm_rd32(gr->engine.subdev.device, 0x1540); +} + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static int +nv40_gr_object_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + int ret = nvkm_gpuobj_new(object->engine->subdev.device, 20, align, + false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, object->oclass); + nvkm_wo32(*pgpuobj, 0x04, 0x00000000); + nvkm_wo32(*pgpuobj, 0x08, 0x00000000); +#ifdef __BIG_ENDIAN + nvkm_mo32(*pgpuobj, 0x08, 0x01000000, 0x01000000); +#endif + nvkm_wo32(*pgpuobj, 0x0c, 0x00000000); + nvkm_wo32(*pgpuobj, 0x10, 0x00000000); + nvkm_done(*pgpuobj); + } + return ret; +} + +const struct nvkm_object_func +nv40_gr_object = { + .bind = nv40_gr_object_bind, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv40_gr_chan_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct nv40_gr_chan *chan = nv40_gr_chan(object); + struct nv40_gr *gr = chan->gr; + int ret = nvkm_gpuobj_new(gr->base.engine.subdev.device, gr->size, + align, true, parent, pgpuobj); + if (ret == 0) { + chan->inst = (*pgpuobj)->addr; + nvkm_kmap(*pgpuobj); + nv40_grctx_fill(gr->base.engine.subdev.device, *pgpuobj); + nvkm_wo32(*pgpuobj, 0x00000, chan->inst >> 4); + nvkm_done(*pgpuobj); + } + return ret; +} + +static int +nv40_gr_chan_fini(struct nvkm_object *object, bool suspend) +{ + struct nv40_gr_chan *chan = nv40_gr_chan(object); + struct nv40_gr *gr = chan->gr; + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 inst = 0x01000000 | chan->inst >> 4; + int ret = 0; + + nvkm_mask(device, 0x400720, 0x00000001, 0x00000000); + + if (nvkm_rd32(device, 0x40032c) == inst) { + if (suspend) { + nvkm_wr32(device, 0x400720, 0x00000000); + nvkm_wr32(device, 0x400784, inst); + nvkm_mask(device, 0x400310, 0x00000020, 0x00000020); + nvkm_mask(device, 0x400304, 0x00000001, 0x00000001); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x400300) & 0x00000001)) + break; + ) < 0) { + u32 insn = nvkm_rd32(device, 0x400308); + nvkm_warn(subdev, "ctxprog timeout %08x\n", insn); + ret = -EBUSY; + } + } + + nvkm_mask(device, 0x40032c, 0x01000000, 0x00000000); + } + + if (nvkm_rd32(device, 0x400330) == inst) + nvkm_mask(device, 0x400330, 0x01000000, 0x00000000); + + nvkm_mask(device, 0x400720, 0x00000001, 0x00000001); + return ret; +} + +static void * +nv40_gr_chan_dtor(struct nvkm_object *object) +{ + struct nv40_gr_chan *chan = nv40_gr_chan(object); + unsigned long flags; + spin_lock_irqsave(&chan->gr->base.engine.lock, flags); + list_del(&chan->head); + spin_unlock_irqrestore(&chan->gr->base.engine.lock, flags); + return chan; +} + +static const struct nvkm_object_func +nv40_gr_chan = { + .dtor = nv40_gr_chan_dtor, + .fini = nv40_gr_chan_fini, + .bind = nv40_gr_chan_bind, +}; + +int +nv40_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv40_gr *gr = nv40_gr(base); + struct nv40_gr_chan *chan; + unsigned long flags; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv40_gr_chan, oclass, &chan->object); + chan->gr = gr; + chan->fifo = fifoch; + *pobject = &chan->object; + + spin_lock_irqsave(&chan->gr->base.engine.lock, flags); + list_add(&chan->head, &gr->chan); + spin_unlock_irqrestore(&chan->gr->base.engine.lock, flags); + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static void +nv40_gr_tile(struct nvkm_gr *base, int i, struct nvkm_fb_tile *tile) +{ + struct nv40_gr *gr = nv40_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nvkm_fifo *fifo = device->fifo; + unsigned long flags; + + nvkm_fifo_pause(fifo, &flags); + nv04_gr_idle(&gr->base); + + switch (device->chipset) { + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x45: + nvkm_wr32(device, NV20_PGRAPH_TSIZE(i), tile->pitch); + nvkm_wr32(device, NV20_PGRAPH_TLIMIT(i), tile->limit); + nvkm_wr32(device, NV20_PGRAPH_TILE(i), tile->addr); + nvkm_wr32(device, NV40_PGRAPH_TSIZE1(i), tile->pitch); + nvkm_wr32(device, NV40_PGRAPH_TLIMIT1(i), tile->limit); + nvkm_wr32(device, NV40_PGRAPH_TILE1(i), tile->addr); + switch (device->chipset) { + case 0x40: + case 0x45: + nvkm_wr32(device, NV20_PGRAPH_ZCOMP(i), tile->zcomp); + nvkm_wr32(device, NV40_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + case 0x41: + case 0x42: + case 0x43: + nvkm_wr32(device, NV41_PGRAPH_ZCOMP0(i), tile->zcomp); + nvkm_wr32(device, NV41_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + default: + break; + } + break; + case 0x47: + case 0x49: + case 0x4b: + nvkm_wr32(device, NV47_PGRAPH_TSIZE(i), tile->pitch); + nvkm_wr32(device, NV47_PGRAPH_TLIMIT(i), tile->limit); + nvkm_wr32(device, NV47_PGRAPH_TILE(i), tile->addr); + nvkm_wr32(device, NV40_PGRAPH_TSIZE1(i), tile->pitch); + nvkm_wr32(device, NV40_PGRAPH_TLIMIT1(i), tile->limit); + nvkm_wr32(device, NV40_PGRAPH_TILE1(i), tile->addr); + nvkm_wr32(device, NV47_PGRAPH_ZCOMP0(i), tile->zcomp); + nvkm_wr32(device, NV47_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + default: + WARN_ON(1); + break; + } + + nvkm_fifo_start(fifo, &flags); +} + +void +nv40_gr_intr(struct nvkm_gr *base) +{ + struct nv40_gr *gr = nv40_gr(base); + struct nv40_gr_chan *temp, *chan = NULL; + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, NV03_PGRAPH_INTR); + u32 nsource = nvkm_rd32(device, NV03_PGRAPH_NSOURCE); + u32 nstatus = nvkm_rd32(device, NV03_PGRAPH_NSTATUS); + u32 inst = nvkm_rd32(device, 0x40032c) & 0x000fffff; + u32 addr = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_ADDR); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nvkm_rd32(device, NV04_PGRAPH_TRAPPED_DATA); + u32 class = nvkm_rd32(device, 0x400160 + subc * 4) & 0xffff; + u32 show = stat; + char msg[128], src[128], sta[128]; + unsigned long flags; + + spin_lock_irqsave(&gr->base.engine.lock, flags); + list_for_each_entry(temp, &gr->chan, head) { + if (temp->inst >> 4 == inst) { + chan = temp; + list_del(&chan->head); + list_add(&chan->head, &gr->chan); + break; + } + } + + if (stat & NV_PGRAPH_INTR_ERROR) { + if (nsource & NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION) { + nvkm_mask(device, 0x402000, 0, 0); + } + } + + nvkm_wr32(device, NV03_PGRAPH_INTR, stat); + nvkm_wr32(device, NV04_PGRAPH_FIFO, 0x00000001); + + if (show) { + nvkm_snprintbf(msg, sizeof(msg), nv10_gr_intr_name, show); + nvkm_snprintbf(src, sizeof(src), nv04_gr_nsource, nsource); + nvkm_snprintbf(sta, sizeof(sta), nv10_gr_nstatus, nstatus); + nvkm_error(subdev, "intr %08x [%s] nsource %08x [%s] " + "nstatus %08x [%s] ch %d [%08x %s] subc %d " + "class %04x mthd %04x data %08x\n", + show, msg, nsource, src, nstatus, sta, + chan ? chan->fifo->chid : -1, inst << 4, + chan ? chan->fifo->object.client->name : "unknown", + subc, class, mthd, data); + } + + spin_unlock_irqrestore(&gr->base.engine.lock, flags); +} + +int +nv40_gr_init(struct nvkm_gr *base) +{ + struct nv40_gr *gr = nv40_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + int ret, i, j; + u32 vramsz; + + /* generate and upload context program */ + ret = nv40_grctx_init(device, &gr->size); + if (ret) + return ret; + + /* No context present currently */ + nvkm_wr32(device, NV40_PGRAPH_CTXCTL_CUR, 0x00000000); + + nvkm_wr32(device, NV03_PGRAPH_INTR , 0xFFFFFFFF); + nvkm_wr32(device, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF); + + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_0, 0x00000000); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_1, 0x401287c0); + nvkm_wr32(device, NV04_PGRAPH_DEBUG_3, 0xe0de8055); + nvkm_wr32(device, NV10_PGRAPH_DEBUG_4, 0x00008000); + nvkm_wr32(device, NV04_PGRAPH_LIMIT_VIOL_PIX, 0x00be3c5f); + + nvkm_wr32(device, NV10_PGRAPH_CTX_CONTROL, 0x10010100); + nvkm_wr32(device, NV10_PGRAPH_STATE , 0xFFFFFFFF); + + j = nvkm_rd32(device, 0x1540) & 0xff; + if (j) { + for (i = 0; !(j & 1); j >>= 1, i++) + ; + nvkm_wr32(device, 0x405000, i); + } + + if (device->chipset == 0x40) { + nvkm_wr32(device, 0x4009b0, 0x83280fff); + nvkm_wr32(device, 0x4009b4, 0x000000a0); + } else { + nvkm_wr32(device, 0x400820, 0x83280eff); + nvkm_wr32(device, 0x400824, 0x000000a0); + } + + switch (device->chipset) { + case 0x40: + case 0x45: + nvkm_wr32(device, 0x4009b8, 0x0078e366); + nvkm_wr32(device, 0x4009bc, 0x0000014c); + break; + case 0x41: + case 0x42: /* pciid also 0x00Cx */ + /* case 0x0120: XXX (pciid) */ + nvkm_wr32(device, 0x400828, 0x007596ff); + nvkm_wr32(device, 0x40082c, 0x00000108); + break; + case 0x43: + nvkm_wr32(device, 0x400828, 0x0072cb77); + nvkm_wr32(device, 0x40082c, 0x00000108); + break; + case 0x44: + case 0x46: /* G72 */ + case 0x4a: + case 0x4c: /* G7x-based C51 */ + case 0x4e: + nvkm_wr32(device, 0x400860, 0); + nvkm_wr32(device, 0x400864, 0); + break; + case 0x47: /* G70 */ + case 0x49: /* G71 */ + case 0x4b: /* G73 */ + nvkm_wr32(device, 0x400828, 0x07830610); + nvkm_wr32(device, 0x40082c, 0x0000016A); + break; + default: + break; + } + + nvkm_wr32(device, 0x400b38, 0x2ffff800); + nvkm_wr32(device, 0x400b3c, 0x00006000); + + /* Tiling related stuff. */ + switch (device->chipset) { + case 0x44: + case 0x4a: + nvkm_wr32(device, 0x400bc4, 0x1003d888); + nvkm_wr32(device, 0x400bbc, 0xb7a7b500); + break; + case 0x46: + nvkm_wr32(device, 0x400bc4, 0x0000e024); + nvkm_wr32(device, 0x400bbc, 0xb7a7b520); + break; + case 0x4c: + case 0x4e: + case 0x67: + nvkm_wr32(device, 0x400bc4, 0x1003d888); + nvkm_wr32(device, 0x400bbc, 0xb7a7b540); + break; + default: + break; + } + + /* begin RAM config */ + vramsz = device->func->resource_size(device, 1) - 1; + switch (device->chipset) { + case 0x40: + nvkm_wr32(device, 0x4009A4, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x4009A8, nvkm_rd32(device, 0x100204)); + nvkm_wr32(device, 0x4069A4, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x4069A8, nvkm_rd32(device, 0x100204)); + nvkm_wr32(device, 0x400820, 0); + nvkm_wr32(device, 0x400824, 0); + nvkm_wr32(device, 0x400864, vramsz); + nvkm_wr32(device, 0x400868, vramsz); + break; + default: + switch (device->chipset) { + case 0x41: + case 0x42: + case 0x43: + case 0x45: + case 0x4e: + case 0x44: + case 0x4a: + nvkm_wr32(device, 0x4009F0, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x4009F4, nvkm_rd32(device, 0x100204)); + break; + default: + nvkm_wr32(device, 0x400DF0, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x400DF4, nvkm_rd32(device, 0x100204)); + break; + } + nvkm_wr32(device, 0x4069F0, nvkm_rd32(device, 0x100200)); + nvkm_wr32(device, 0x4069F4, nvkm_rd32(device, 0x100204)); + nvkm_wr32(device, 0x400840, 0); + nvkm_wr32(device, 0x400844, 0); + nvkm_wr32(device, 0x4008A0, vramsz); + nvkm_wr32(device, 0x4008A4, vramsz); + break; + } + + return 0; +} + +int +nv40_gr_new_(const struct nvkm_gr_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + struct nv40_gr *gr; + + if (!(gr = kzalloc(sizeof(*gr), GFP_KERNEL))) + return -ENOMEM; + *pgr = &gr->base; + INIT_LIST_HEAD(&gr->chan); + + return nvkm_gr_ctor(func, device, type, inst, true, &gr->base); +} + +static const struct nvkm_gr_func +nv40_gr = { + .init = nv40_gr_init, + .intr = nv40_gr_intr, + .tile = nv40_gr_tile, + .units = nv40_gr_units, + .chan_new = nv40_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv40_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv40_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv40_gr_object }, /* null */ + { -1, -1, 0x0039, &nv40_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv40_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv40_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv40_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv40_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv40_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv40_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv40_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv40_gr_object }, /* imageblit */ + { -1, -1, 0x3062, &nv40_gr_object }, /* surf2d (nv40) */ + { -1, -1, 0x3089, &nv40_gr_object }, /* sifm (nv40) */ + { -1, -1, 0x309e, &nv40_gr_object }, /* swzsurf (nv40) */ + { -1, -1, 0x4097, &nv40_gr_object }, /* curie */ + {} + } +}; + +int +nv40_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv40_gr_new_(&nv40_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h new file mode 100644 index 000000000..f3d3d3a5a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV40_GR_H__ +#define __NV40_GR_H__ +#define nv40_gr(p) container_of((p), struct nv40_gr, base) +#include "priv.h" + +struct nv40_gr { + struct nvkm_gr base; + u32 size; + struct list_head chan; +}; + +int nv40_gr_new_(const struct nvkm_gr_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_gr **); +int nv40_gr_init(struct nvkm_gr *); +void nv40_gr_intr(struct nvkm_gr *); +u64 nv40_gr_units(struct nvkm_gr *); + +#define nv40_gr_chan(p) container_of((p), struct nv40_gr_chan, object) +#include + +struct nv40_gr_chan { + struct nvkm_object object; + struct nv40_gr *gr; + struct nvkm_fifo_chan *fifo; + u32 inst; + struct list_head head; +}; + +int nv40_gr_chan_new(struct nvkm_gr *, struct nvkm_fifo_chan *, + const struct nvkm_oclass *, struct nvkm_object **); + +extern const struct nvkm_object_func nv40_gr_object; + +/* returns 1 if device is one of the nv4x using the 0x4497 object class, + * helpful to determine a number of other hardware features + */ +static inline int +nv44_gr_class(struct nvkm_device *device) +{ + if ((device->chipset & 0xf0) == 0x60) + return 1; + + return !(0x0aaf & (1 << (device->chipset & 0x0f))); +} + +int nv40_grctx_init(struct nvkm_device *, u32 *size); +void nv40_grctx_fill(struct nvkm_device *, struct nvkm_gpuobj *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv44.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv44.c new file mode 100644 index 000000000..22b6a38a7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv44.c @@ -0,0 +1,108 @@ +/* + * 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 "nv40.h" +#include "regs.h" + +#include +#include + +static void +nv44_gr_tile(struct nvkm_gr *base, int i, struct nvkm_fb_tile *tile) +{ + struct nv40_gr *gr = nv40_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + struct nvkm_fifo *fifo = device->fifo; + unsigned long flags; + + nvkm_fifo_pause(fifo, &flags); + nv04_gr_idle(&gr->base); + + switch (device->chipset) { + case 0x44: + case 0x4a: + nvkm_wr32(device, NV20_PGRAPH_TSIZE(i), tile->pitch); + nvkm_wr32(device, NV20_PGRAPH_TLIMIT(i), tile->limit); + nvkm_wr32(device, NV20_PGRAPH_TILE(i), tile->addr); + break; + case 0x46: + case 0x4c: + case 0x63: + case 0x67: + case 0x68: + nvkm_wr32(device, NV47_PGRAPH_TSIZE(i), tile->pitch); + nvkm_wr32(device, NV47_PGRAPH_TLIMIT(i), tile->limit); + nvkm_wr32(device, NV47_PGRAPH_TILE(i), tile->addr); + nvkm_wr32(device, NV40_PGRAPH_TSIZE1(i), tile->pitch); + nvkm_wr32(device, NV40_PGRAPH_TLIMIT1(i), tile->limit); + nvkm_wr32(device, NV40_PGRAPH_TILE1(i), tile->addr); + break; + case 0x4e: + nvkm_wr32(device, NV20_PGRAPH_TSIZE(i), tile->pitch); + nvkm_wr32(device, NV20_PGRAPH_TLIMIT(i), tile->limit); + nvkm_wr32(device, NV20_PGRAPH_TILE(i), tile->addr); + nvkm_wr32(device, NV40_PGRAPH_TSIZE1(i), tile->pitch); + nvkm_wr32(device, NV40_PGRAPH_TLIMIT1(i), tile->limit); + nvkm_wr32(device, NV40_PGRAPH_TILE1(i), tile->addr); + break; + default: + WARN_ON(1); + break; + } + + nvkm_fifo_start(fifo, &flags); +} + +static const struct nvkm_gr_func +nv44_gr = { + .init = nv40_gr_init, + .intr = nv40_gr_intr, + .tile = nv44_gr_tile, + .units = nv40_gr_units, + .chan_new = nv40_gr_chan_new, + .sclass = { + { -1, -1, 0x0012, &nv40_gr_object }, /* beta1 */ + { -1, -1, 0x0019, &nv40_gr_object }, /* clip */ + { -1, -1, 0x0030, &nv40_gr_object }, /* null */ + { -1, -1, 0x0039, &nv40_gr_object }, /* m2mf */ + { -1, -1, 0x0043, &nv40_gr_object }, /* rop */ + { -1, -1, 0x0044, &nv40_gr_object }, /* patt */ + { -1, -1, 0x004a, &nv40_gr_object }, /* gdi */ + { -1, -1, 0x0062, &nv40_gr_object }, /* surf2d */ + { -1, -1, 0x0072, &nv40_gr_object }, /* beta4 */ + { -1, -1, 0x0089, &nv40_gr_object }, /* sifm */ + { -1, -1, 0x008a, &nv40_gr_object }, /* ifc */ + { -1, -1, 0x009f, &nv40_gr_object }, /* imageblit */ + { -1, -1, 0x3062, &nv40_gr_object }, /* surf2d (nv40) */ + { -1, -1, 0x3089, &nv40_gr_object }, /* sifm (nv40) */ + { -1, -1, 0x309e, &nv40_gr_object }, /* swzsurf (nv40) */ + { -1, -1, 0x4497, &nv40_gr_object }, /* curie */ + {} + } +}; + +int +nv44_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv40_gr_new_(&nv44_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c new file mode 100644 index 000000000..563a10097 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c @@ -0,0 +1,796 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include + +#include + +u64 +nv50_gr_units(struct nvkm_gr *gr) +{ + return nvkm_rd32(gr->engine.subdev.device, 0x1540); +} + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static int +nv50_gr_object_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + int ret = nvkm_gpuobj_new(object->engine->subdev.device, 16, + align, false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, object->oclass); + nvkm_wo32(*pgpuobj, 0x04, 0x00000000); + nvkm_wo32(*pgpuobj, 0x08, 0x00000000); + nvkm_wo32(*pgpuobj, 0x0c, 0x00000000); + nvkm_done(*pgpuobj); + } + return ret; +} + +const struct nvkm_object_func +nv50_gr_object = { + .bind = nv50_gr_object_bind, +}; + +/******************************************************************************* + * PGRAPH context + ******************************************************************************/ + +static int +nv50_gr_chan_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct nv50_gr *gr = nv50_gr_chan(object)->gr; + int ret = nvkm_gpuobj_new(gr->base.engine.subdev.device, gr->size, + align, true, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nv50_grctx_fill(gr->base.engine.subdev.device, *pgpuobj); + nvkm_done(*pgpuobj); + } + return ret; +} + +static const struct nvkm_object_func +nv50_gr_chan = { + .bind = nv50_gr_chan_bind, +}; + +int +nv50_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv50_gr *gr = nv50_gr(base); + struct nv50_gr_chan *chan; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv50_gr_chan, oclass, &chan->object); + chan->gr = gr; + *pobject = &chan->object; + return 0; +} + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_bitfield nv50_mp_exec_errors[] = { + { 0x01, "STACK_UNDERFLOW" }, + { 0x02, "STACK_MISMATCH" }, + { 0x04, "QUADON_ACTIVE" }, + { 0x08, "TIMEOUT" }, + { 0x10, "INVALID_OPCODE" }, + { 0x20, "PM_OVERFLOW" }, + { 0x40, "BREAKPOINT" }, + {} +}; + +static const struct nvkm_bitfield nv50_mpc_traps[] = { + { 0x0000001, "LOCAL_LIMIT_READ" }, + { 0x0000010, "LOCAL_LIMIT_WRITE" }, + { 0x0000040, "STACK_LIMIT" }, + { 0x0000100, "GLOBAL_LIMIT_READ" }, + { 0x0001000, "GLOBAL_LIMIT_WRITE" }, + { 0x0010000, "MP0" }, + { 0x0020000, "MP1" }, + { 0x0040000, "GLOBAL_LIMIT_RED" }, + { 0x0400000, "GLOBAL_LIMIT_ATOM" }, + { 0x4000000, "MP2" }, + {} +}; + +static const struct nvkm_bitfield nv50_tex_traps[] = { + { 0x00000001, "" }, /* any bit set? */ + { 0x00000002, "FAULT" }, + { 0x00000004, "STORAGE_TYPE_MISMATCH" }, + { 0x00000008, "LINEAR_MISMATCH" }, + { 0x00000020, "WRONG_MEMTYPE" }, + {} +}; + +static const struct nvkm_bitfield nv50_gr_trap_m2mf[] = { + { 0x00000001, "NOTIFY" }, + { 0x00000002, "IN" }, + { 0x00000004, "OUT" }, + {} +}; + +static const struct nvkm_bitfield nv50_gr_trap_vfetch[] = { + { 0x00000001, "FAULT" }, + {} +}; + +static const struct nvkm_bitfield nv50_gr_trap_strmout[] = { + { 0x00000001, "FAULT" }, + {} +}; + +static const struct nvkm_bitfield nv50_gr_trap_ccache[] = { + { 0x00000001, "FAULT" }, + {} +}; + +/* There must be a *lot* of these. Will take some time to gather them up. */ +const struct nvkm_enum nv50_data_error_names[] = { + { 0x00000003, "INVALID_OPERATION", NULL }, + { 0x00000004, "INVALID_VALUE", NULL }, + { 0x00000005, "INVALID_ENUM", NULL }, + { 0x00000008, "INVALID_OBJECT", NULL }, + { 0x00000009, "READ_ONLY_OBJECT", NULL }, + { 0x0000000a, "SUPERVISOR_OBJECT", NULL }, + { 0x0000000b, "INVALID_ADDRESS_ALIGNMENT", NULL }, + { 0x0000000c, "INVALID_BITFIELD", NULL }, + { 0x0000000d, "BEGIN_END_ACTIVE", NULL }, + { 0x0000000e, "SEMANTIC_COLOR_BACK_OVER_LIMIT", NULL }, + { 0x0000000f, "VIEWPORT_ID_NEEDS_GP", NULL }, + { 0x00000010, "RT_DOUBLE_BIND", NULL }, + { 0x00000011, "RT_TYPES_MISMATCH", NULL }, + { 0x00000012, "RT_LINEAR_WITH_ZETA", NULL }, + { 0x00000015, "FP_TOO_FEW_REGS", NULL }, + { 0x00000016, "ZETA_FORMAT_CSAA_MISMATCH", NULL }, + { 0x00000017, "RT_LINEAR_WITH_MSAA", NULL }, + { 0x00000018, "FP_INTERPOLANT_START_OVER_LIMIT", NULL }, + { 0x00000019, "SEMANTIC_LAYER_OVER_LIMIT", NULL }, + { 0x0000001a, "RT_INVALID_ALIGNMENT", NULL }, + { 0x0000001b, "SAMPLER_OVER_LIMIT", NULL }, + { 0x0000001c, "TEXTURE_OVER_LIMIT", NULL }, + { 0x0000001e, "GP_TOO_MANY_OUTPUTS", NULL }, + { 0x0000001f, "RT_BPP128_WITH_MS8", NULL }, + { 0x00000021, "Z_OUT_OF_BOUNDS", NULL }, + { 0x00000023, "XY_OUT_OF_BOUNDS", NULL }, + { 0x00000024, "VP_ZERO_INPUTS", NULL }, + { 0x00000027, "CP_MORE_PARAMS_THAN_SHARED", NULL }, + { 0x00000028, "CP_NO_REG_SPACE_STRIPED", NULL }, + { 0x00000029, "CP_NO_REG_SPACE_PACKED", NULL }, + { 0x0000002a, "CP_NOT_ENOUGH_WARPS", NULL }, + { 0x0000002b, "CP_BLOCK_SIZE_MISMATCH", NULL }, + { 0x0000002c, "CP_NOT_ENOUGH_LOCAL_WARPS", NULL }, + { 0x0000002d, "CP_NOT_ENOUGH_STACK_WARPS", NULL }, + { 0x0000002e, "CP_NO_BLOCKDIM_LATCH", NULL }, + { 0x00000031, "ENG2D_FORMAT_MISMATCH", NULL }, + { 0x0000003f, "PRIMITIVE_ID_NEEDS_GP", NULL }, + { 0x00000044, "SEMANTIC_VIEWPORT_OVER_LIMIT", NULL }, + { 0x00000045, "SEMANTIC_COLOR_FRONT_OVER_LIMIT", NULL }, + { 0x00000046, "LAYER_ID_NEEDS_GP", NULL }, + { 0x00000047, "SEMANTIC_CLIP_OVER_LIMIT", NULL }, + { 0x00000048, "SEMANTIC_PTSZ_OVER_LIMIT", NULL }, + {} +}; + +static const struct nvkm_bitfield nv50_gr_intr_name[] = { + { 0x00000001, "NOTIFY" }, + { 0x00000002, "COMPUTE_QUERY" }, + { 0x00000010, "ILLEGAL_MTHD" }, + { 0x00000020, "ILLEGAL_CLASS" }, + { 0x00000040, "DOUBLE_NOTIFY" }, + { 0x00001000, "CONTEXT_SWITCH" }, + { 0x00010000, "BUFFER_NOTIFY" }, + { 0x00100000, "DATA_ERROR" }, + { 0x00200000, "TRAP" }, + { 0x01000000, "SINGLE_STEP" }, + {} +}; + +static const struct nvkm_bitfield nv50_gr_trap_prop[] = { + { 0x00000004, "SURF_WIDTH_OVERRUN" }, + { 0x00000008, "SURF_HEIGHT_OVERRUN" }, + { 0x00000010, "DST2D_FAULT" }, + { 0x00000020, "ZETA_FAULT" }, + { 0x00000040, "RT_FAULT" }, + { 0x00000080, "CUDA_FAULT" }, + { 0x00000100, "DST2D_STORAGE_TYPE_MISMATCH" }, + { 0x00000200, "ZETA_STORAGE_TYPE_MISMATCH" }, + { 0x00000400, "RT_STORAGE_TYPE_MISMATCH" }, + { 0x00000800, "DST2D_LINEAR_MISMATCH" }, + { 0x00001000, "RT_LINEAR_MISMATCH" }, + {} +}; + +static void +nv50_gr_prop_trap(struct nv50_gr *gr, u32 ustatus_addr, u32 ustatus, u32 tp) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 e0c = nvkm_rd32(device, ustatus_addr + 0x04); + u32 e10 = nvkm_rd32(device, ustatus_addr + 0x08); + u32 e14 = nvkm_rd32(device, ustatus_addr + 0x0c); + u32 e18 = nvkm_rd32(device, ustatus_addr + 0x10); + u32 e1c = nvkm_rd32(device, ustatus_addr + 0x14); + u32 e20 = nvkm_rd32(device, ustatus_addr + 0x18); + u32 e24 = nvkm_rd32(device, ustatus_addr + 0x1c); + char msg[128]; + + /* CUDA memory: l[], g[] or stack. */ + if (ustatus & 0x00000080) { + if (e18 & 0x80000000) { + /* g[] read fault? */ + nvkm_error(subdev, "TRAP_PROP - TP %d - CUDA_FAULT - Global read fault at address %02x%08x\n", + tp, e14, e10 | ((e18 >> 24) & 0x1f)); + e18 &= ~0x1f000000; + } else if (e18 & 0xc) { + /* g[] write fault? */ + nvkm_error(subdev, "TRAP_PROP - TP %d - CUDA_FAULT - Global write fault at address %02x%08x\n", + tp, e14, e10 | ((e18 >> 7) & 0x1f)); + e18 &= ~0x00000f80; + } else { + nvkm_error(subdev, "TRAP_PROP - TP %d - Unknown CUDA fault at address %02x%08x\n", + tp, e14, e10); + } + ustatus &= ~0x00000080; + } + if (ustatus) { + nvkm_snprintbf(msg, sizeof(msg), nv50_gr_trap_prop, ustatus); + nvkm_error(subdev, "TRAP_PROP - TP %d - %08x [%s] - " + "Address %02x%08x\n", + tp, ustatus, msg, e14, e10); + } + nvkm_error(subdev, "TRAP_PROP - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n", + tp, e0c, e18, e1c, e20, e24); +} + +static void +nv50_gr_mp_trap(struct nv50_gr *gr, int tpid, int display) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 units = nvkm_rd32(device, 0x1540); + u32 addr, mp10, status, pc, oplow, ophigh; + char msg[128]; + int i; + int mps = 0; + for (i = 0; i < 4; i++) { + if (!(units & 1 << (i+24))) + continue; + if (device->chipset < 0xa0) + addr = 0x408200 + (tpid << 12) + (i << 7); + else + addr = 0x408100 + (tpid << 11) + (i << 7); + mp10 = nvkm_rd32(device, addr + 0x10); + status = nvkm_rd32(device, addr + 0x14); + if (!status) + continue; + if (display) { + nvkm_rd32(device, addr + 0x20); + pc = nvkm_rd32(device, addr + 0x24); + oplow = nvkm_rd32(device, addr + 0x70); + ophigh = nvkm_rd32(device, addr + 0x74); + nvkm_snprintbf(msg, sizeof(msg), + nv50_mp_exec_errors, status); + nvkm_error(subdev, "TRAP_MP_EXEC - TP %d MP %d: " + "%08x [%s] at %06x warp %d, " + "opcode %08x %08x\n", + tpid, i, status, msg, pc & 0xffffff, + pc >> 24, oplow, ophigh); + } + nvkm_wr32(device, addr + 0x10, mp10); + nvkm_wr32(device, addr + 0x14, 0); + mps++; + } + if (!mps && display) + nvkm_error(subdev, "TRAP_MP_EXEC - TP %d: " + "No MPs claiming errors?\n", tpid); +} + +static void +nv50_gr_tp_trap(struct nv50_gr *gr, int type, u32 ustatus_old, + u32 ustatus_new, int display, const char *name) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 units = nvkm_rd32(device, 0x1540); + int tps = 0; + int i, r; + char msg[128]; + u32 ustatus_addr, ustatus; + for (i = 0; i < 16; i++) { + if (!(units & (1 << i))) + continue; + if (device->chipset < 0xa0) + ustatus_addr = ustatus_old + (i << 12); + else + ustatus_addr = ustatus_new + (i << 11); + ustatus = nvkm_rd32(device, ustatus_addr) & 0x7fffffff; + if (!ustatus) + continue; + tps++; + switch (type) { + case 6: /* texture error... unknown for now */ + if (display) { + nvkm_error(subdev, "magic set %d:\n", i); + for (r = ustatus_addr + 4; r <= ustatus_addr + 0x10; r += 4) + nvkm_error(subdev, "\t%08x: %08x\n", r, + nvkm_rd32(device, r)); + if (ustatus) { + nvkm_snprintbf(msg, sizeof(msg), + nv50_tex_traps, ustatus); + nvkm_error(subdev, + "%s - TP%d: %08x [%s]\n", + name, i, ustatus, msg); + ustatus = 0; + } + } + break; + case 7: /* MP error */ + if (ustatus & 0x04030000) { + nv50_gr_mp_trap(gr, i, display); + ustatus &= ~0x04030000; + } + if (ustatus && display) { + nvkm_snprintbf(msg, sizeof(msg), + nv50_mpc_traps, ustatus); + nvkm_error(subdev, "%s - TP%d: %08x [%s]\n", + name, i, ustatus, msg); + ustatus = 0; + } + break; + case 8: /* PROP error */ + if (display) + nv50_gr_prop_trap( + gr, ustatus_addr, ustatus, i); + ustatus = 0; + break; + } + if (ustatus) { + if (display) + nvkm_error(subdev, "%s - TP%d: Unhandled ustatus %08x\n", name, i, ustatus); + } + nvkm_wr32(device, ustatus_addr, 0xc0000000); + } + + if (!tps && display) + nvkm_warn(subdev, "%s - No TPs claiming errors?\n", name); +} + +static int +nv50_gr_trap_handler(struct nv50_gr *gr, u32 display, + int chid, u64 inst, const char *name) +{ + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 status = nvkm_rd32(device, 0x400108); + u32 ustatus; + char msg[128]; + + if (!status && display) { + nvkm_error(subdev, "TRAP: no units reporting traps?\n"); + return 1; + } + + /* DISPATCH: Relays commands to other units and handles NOTIFY, + * COND, QUERY. If you get a trap from it, the command is still stuck + * in DISPATCH and you need to do something about it. */ + if (status & 0x001) { + ustatus = nvkm_rd32(device, 0x400804) & 0x7fffffff; + if (!ustatus && display) { + nvkm_error(subdev, "TRAP_DISPATCH - no ustatus?\n"); + } + + nvkm_wr32(device, 0x400500, 0x00000000); + + /* Known to be triggered by screwed up NOTIFY and COND... */ + if (ustatus & 0x00000001) { + u32 addr = nvkm_rd32(device, 0x400808); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 datal = nvkm_rd32(device, 0x40080c); + u32 datah = nvkm_rd32(device, 0x400810); + u32 class = nvkm_rd32(device, 0x400814); + u32 r848 = nvkm_rd32(device, 0x400848); + + nvkm_error(subdev, "TRAP DISPATCH_FAULT\n"); + if (display && (addr & 0x80000000)) { + nvkm_error(subdev, + "ch %d [%010llx %s] subc %d " + "class %04x mthd %04x data %08x%08x " + "400808 %08x 400848 %08x\n", + chid, inst, name, subc, class, mthd, + datah, datal, addr, r848); + } else + if (display) { + nvkm_error(subdev, "no stuck command?\n"); + } + + nvkm_wr32(device, 0x400808, 0); + nvkm_wr32(device, 0x4008e8, nvkm_rd32(device, 0x4008e8) & 3); + nvkm_wr32(device, 0x400848, 0); + ustatus &= ~0x00000001; + } + + if (ustatus & 0x00000002) { + u32 addr = nvkm_rd32(device, 0x40084c); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nvkm_rd32(device, 0x40085c); + u32 class = nvkm_rd32(device, 0x400814); + + nvkm_error(subdev, "TRAP DISPATCH_QUERY\n"); + if (display && (addr & 0x80000000)) { + nvkm_error(subdev, + "ch %d [%010llx %s] subc %d " + "class %04x mthd %04x data %08x " + "40084c %08x\n", chid, inst, name, + subc, class, mthd, data, addr); + } else + if (display) { + nvkm_error(subdev, "no stuck command?\n"); + } + + nvkm_wr32(device, 0x40084c, 0); + ustatus &= ~0x00000002; + } + + if (ustatus && display) { + nvkm_error(subdev, "TRAP_DISPATCH " + "(unknown %08x)\n", ustatus); + } + + nvkm_wr32(device, 0x400804, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x001); + status &= ~0x001; + if (!status) + return 0; + } + + /* M2MF: Memory to memory copy engine. */ + if (status & 0x002) { + u32 ustatus = nvkm_rd32(device, 0x406800) & 0x7fffffff; + if (display) { + nvkm_snprintbf(msg, sizeof(msg), + nv50_gr_trap_m2mf, ustatus); + nvkm_error(subdev, "TRAP_M2MF %08x [%s]\n", + ustatus, msg); + nvkm_error(subdev, "TRAP_M2MF %08x %08x %08x %08x\n", + nvkm_rd32(device, 0x406804), + nvkm_rd32(device, 0x406808), + nvkm_rd32(device, 0x40680c), + nvkm_rd32(device, 0x406810)); + } + + /* No sane way found yet -- just reset the bugger. */ + nvkm_wr32(device, 0x400040, 2); + nvkm_wr32(device, 0x400040, 0); + nvkm_wr32(device, 0x406800, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x002); + status &= ~0x002; + } + + /* VFETCH: Fetches data from vertex buffers. */ + if (status & 0x004) { + u32 ustatus = nvkm_rd32(device, 0x400c04) & 0x7fffffff; + if (display) { + nvkm_snprintbf(msg, sizeof(msg), + nv50_gr_trap_vfetch, ustatus); + nvkm_error(subdev, "TRAP_VFETCH %08x [%s]\n", + ustatus, msg); + nvkm_error(subdev, "TRAP_VFETCH %08x %08x %08x %08x\n", + nvkm_rd32(device, 0x400c00), + nvkm_rd32(device, 0x400c08), + nvkm_rd32(device, 0x400c0c), + nvkm_rd32(device, 0x400c10)); + } + + nvkm_wr32(device, 0x400c04, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x004); + status &= ~0x004; + } + + /* STRMOUT: DirectX streamout / OpenGL transform feedback. */ + if (status & 0x008) { + ustatus = nvkm_rd32(device, 0x401800) & 0x7fffffff; + if (display) { + nvkm_snprintbf(msg, sizeof(msg), + nv50_gr_trap_strmout, ustatus); + nvkm_error(subdev, "TRAP_STRMOUT %08x [%s]\n", + ustatus, msg); + nvkm_error(subdev, "TRAP_STRMOUT %08x %08x %08x %08x\n", + nvkm_rd32(device, 0x401804), + nvkm_rd32(device, 0x401808), + nvkm_rd32(device, 0x40180c), + nvkm_rd32(device, 0x401810)); + } + + /* No sane way found yet -- just reset the bugger. */ + nvkm_wr32(device, 0x400040, 0x80); + nvkm_wr32(device, 0x400040, 0); + nvkm_wr32(device, 0x401800, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x008); + status &= ~0x008; + } + + /* CCACHE: Handles code and c[] caches and fills them. */ + if (status & 0x010) { + ustatus = nvkm_rd32(device, 0x405018) & 0x7fffffff; + if (display) { + nvkm_snprintbf(msg, sizeof(msg), + nv50_gr_trap_ccache, ustatus); + nvkm_error(subdev, "TRAP_CCACHE %08x [%s]\n", + ustatus, msg); + nvkm_error(subdev, "TRAP_CCACHE %08x %08x %08x %08x " + "%08x %08x %08x\n", + nvkm_rd32(device, 0x405000), + nvkm_rd32(device, 0x405004), + nvkm_rd32(device, 0x405008), + nvkm_rd32(device, 0x40500c), + nvkm_rd32(device, 0x405010), + nvkm_rd32(device, 0x405014), + nvkm_rd32(device, 0x40501c)); + } + + nvkm_wr32(device, 0x405018, 0xc0000000); + nvkm_wr32(device, 0x400108, 0x010); + status &= ~0x010; + } + + /* Unknown, not seen yet... 0x402000 is the only trap status reg + * remaining, so try to handle it anyway. Perhaps related to that + * unknown DMA slot on tesla? */ + if (status & 0x20) { + ustatus = nvkm_rd32(device, 0x402000) & 0x7fffffff; + if (display) + nvkm_error(subdev, "TRAP_UNKC04 %08x\n", ustatus); + nvkm_wr32(device, 0x402000, 0xc0000000); + /* no status modifiction on purpose */ + } + + /* TEXTURE: CUDA texturing units */ + if (status & 0x040) { + nv50_gr_tp_trap(gr, 6, 0x408900, 0x408600, display, + "TRAP_TEXTURE"); + nvkm_wr32(device, 0x400108, 0x040); + status &= ~0x040; + } + + /* MP: CUDA execution engines. */ + if (status & 0x080) { + nv50_gr_tp_trap(gr, 7, 0x408314, 0x40831c, display, + "TRAP_MP"); + nvkm_wr32(device, 0x400108, 0x080); + status &= ~0x080; + } + + /* PROP: Handles TP-initiated uncached memory accesses: + * l[], g[], stack, 2d surfaces, render targets. */ + if (status & 0x100) { + nv50_gr_tp_trap(gr, 8, 0x408e08, 0x408708, display, + "TRAP_PROP"); + nvkm_wr32(device, 0x400108, 0x100); + status &= ~0x100; + } + + if (status) { + if (display) + nvkm_error(subdev, "TRAP: unknown %08x\n", status); + nvkm_wr32(device, 0x400108, status); + } + + return 1; +} + +void +nv50_gr_intr(struct nvkm_gr *base) +{ + struct nv50_gr *gr = nv50_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fifo_chan *chan; + u32 stat = nvkm_rd32(device, 0x400100); + u32 inst = nvkm_rd32(device, 0x40032c) & 0x0fffffff; + u32 addr = nvkm_rd32(device, 0x400704); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00001ffc); + u32 data = nvkm_rd32(device, 0x400708); + u32 class = nvkm_rd32(device, 0x400814); + u32 show = stat, show_bitfield = stat; + const struct nvkm_enum *en; + unsigned long flags; + const char *name = "unknown"; + char msg[128]; + int chid = -1; + + chan = nvkm_fifo_chan_inst(device->fifo, (u64)inst << 12, &flags); + if (chan) { + name = chan->object.client->name; + chid = chan->chid; + } + + if (show & 0x00100000) { + u32 ecode = nvkm_rd32(device, 0x400110); + en = nvkm_enum_find(nv50_data_error_names, ecode); + nvkm_error(subdev, "DATA_ERROR %08x [%s]\n", + ecode, en ? en->name : ""); + show_bitfield &= ~0x00100000; + } + + if (stat & 0x00200000) { + if (!nv50_gr_trap_handler(gr, show, chid, (u64)inst << 12, name)) + show &= ~0x00200000; + show_bitfield &= ~0x00200000; + } + + nvkm_wr32(device, 0x400100, stat); + nvkm_wr32(device, 0x400500, 0x00010001); + + if (show) { + show &= show_bitfield; + nvkm_snprintbf(msg, sizeof(msg), nv50_gr_intr_name, show); + nvkm_error(subdev, "%08x [%s] ch %d [%010llx %s] subc %d " + "class %04x mthd %04x data %08x\n", + stat, msg, chid, (u64)inst << 12, name, + subc, class, mthd, data); + } + + if (nvkm_rd32(device, 0x400824) & (1 << 31)) + nvkm_wr32(device, 0x400824, nvkm_rd32(device, 0x400824) & ~(1 << 31)); + + nvkm_fifo_chan_put(device->fifo, flags, &chan); +} + +int +nv50_gr_init(struct nvkm_gr *base) +{ + struct nv50_gr *gr = nv50_gr(base); + struct nvkm_device *device = gr->base.engine.subdev.device; + int ret, units, i; + + /* NV_PGRAPH_DEBUG_3_HW_CTX_SWITCH_ENABLED */ + nvkm_wr32(device, 0x40008c, 0x00000004); + + /* reset/enable traps and interrupts */ + nvkm_wr32(device, 0x400804, 0xc0000000); + nvkm_wr32(device, 0x406800, 0xc0000000); + nvkm_wr32(device, 0x400c04, 0xc0000000); + nvkm_wr32(device, 0x401800, 0xc0000000); + nvkm_wr32(device, 0x405018, 0xc0000000); + nvkm_wr32(device, 0x402000, 0xc0000000); + + units = nvkm_rd32(device, 0x001540); + for (i = 0; i < 16; i++) { + if (!(units & (1 << i))) + continue; + + if (device->chipset < 0xa0) { + nvkm_wr32(device, 0x408900 + (i << 12), 0xc0000000); + nvkm_wr32(device, 0x408e08 + (i << 12), 0xc0000000); + nvkm_wr32(device, 0x408314 + (i << 12), 0xc0000000); + } else { + nvkm_wr32(device, 0x408600 + (i << 11), 0xc0000000); + nvkm_wr32(device, 0x408708 + (i << 11), 0xc0000000); + nvkm_wr32(device, 0x40831c + (i << 11), 0xc0000000); + } + } + + nvkm_wr32(device, 0x400108, 0xffffffff); + nvkm_wr32(device, 0x400138, 0xffffffff); + nvkm_wr32(device, 0x400100, 0xffffffff); + nvkm_wr32(device, 0x40013c, 0xffffffff); + nvkm_wr32(device, 0x400500, 0x00010001); + + /* upload context program, initialise ctxctl defaults */ + ret = nv50_grctx_init(device, &gr->size); + if (ret) + return ret; + + nvkm_wr32(device, 0x400824, 0x00000000); + nvkm_wr32(device, 0x400828, 0x00000000); + nvkm_wr32(device, 0x40082c, 0x00000000); + nvkm_wr32(device, 0x400830, 0x00000000); + nvkm_wr32(device, 0x40032c, 0x00000000); + nvkm_wr32(device, 0x400330, 0x00000000); + + /* some unknown zcull magic */ + switch (device->chipset & 0xf0) { + case 0x50: + case 0x80: + case 0x90: + nvkm_wr32(device, 0x402ca8, 0x00000800); + break; + case 0xa0: + default: + if (device->chipset == 0xa0 || + device->chipset == 0xaa || + device->chipset == 0xac) { + nvkm_wr32(device, 0x402ca8, 0x00000802); + } else { + nvkm_wr32(device, 0x402cc0, 0x00000000); + nvkm_wr32(device, 0x402ca8, 0x00000002); + } + + break; + } + + /* zero out zcull regions */ + for (i = 0; i < 8; i++) { + nvkm_wr32(device, 0x402c20 + (i * 0x10), 0x00000000); + nvkm_wr32(device, 0x402c24 + (i * 0x10), 0x00000000); + nvkm_wr32(device, 0x402c28 + (i * 0x10), 0x00000000); + nvkm_wr32(device, 0x402c2c + (i * 0x10), 0x00000000); + } + + return 0; +} + +int +nv50_gr_new_(const struct nvkm_gr_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + struct nv50_gr *gr; + + if (!(gr = kzalloc(sizeof(*gr), GFP_KERNEL))) + return -ENOMEM; + spin_lock_init(&gr->lock); + *pgr = &gr->base; + + return nvkm_gr_ctor(func, device, type, inst, true, &gr->base); +} + +static const struct nvkm_gr_func +nv50_gr = { + .init = nv50_gr_init, + .intr = nv50_gr_intr, + .chan_new = nv50_gr_chan_new, + .units = nv50_gr_units, + .sclass = { + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_TESLA, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + {} + } +}; + +int +nv50_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return nv50_gr_new_(&nv50_gr, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h new file mode 100644 index 000000000..84388c42e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_GR_H__ +#define __NV50_GR_H__ +#define nv50_gr(p) container_of((p), struct nv50_gr, base) +#include "priv.h" + +struct nv50_gr { + struct nvkm_gr base; + const struct nv50_gr_func *func; + spinlock_t lock; + u32 size; +}; + +int nv50_gr_new_(const struct nvkm_gr_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_gr **); +int nv50_gr_init(struct nvkm_gr *); +void nv50_gr_intr(struct nvkm_gr *); +u64 nv50_gr_units(struct nvkm_gr *); + +int g84_gr_tlb_flush(struct nvkm_gr *); + +#define nv50_gr_chan(p) container_of((p), struct nv50_gr_chan, object) +#include + +struct nv50_gr_chan { + struct nvkm_object object; + struct nv50_gr *gr; +}; + +int nv50_gr_chan_new(struct nvkm_gr *, struct nvkm_fifo_chan *, + const struct nvkm_oclass *, struct nvkm_object **); + +extern const struct nvkm_object_func nv50_gr_object; + +int nv50_grctx_init(struct nvkm_device *, u32 *size); +void nv50_grctx_fill(struct nvkm_device *, struct nvkm_gpuobj *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h new file mode 100644 index 000000000..9b2c66e8b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_GR_PRIV_H__ +#define __NVKM_GR_PRIV_H__ +#define nvkm_gr(p) container_of((p), struct nvkm_gr, engine) +#include +#include +struct nvkm_fb_tile; +struct nvkm_fifo_chan; + +int nvkm_gr_ctor(const struct nvkm_gr_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + bool enable, struct nvkm_gr *); + +bool nv04_gr_idle(struct nvkm_gr *); + +struct nvkm_gr_func { + void *(*dtor)(struct nvkm_gr *); + int (*oneinit)(struct nvkm_gr *); + int (*init)(struct nvkm_gr *); + int (*fini)(struct nvkm_gr *, bool); + void (*intr)(struct nvkm_gr *); + void (*tile)(struct nvkm_gr *, int region, struct nvkm_fb_tile *); + int (*tlb_flush)(struct nvkm_gr *); + int (*chan_new)(struct nvkm_gr *, struct nvkm_fifo_chan *, + const struct nvkm_oclass *, struct nvkm_object **); + int (*object_get)(struct nvkm_gr *, int, struct nvkm_sclass *); + /* Returns chipset-specific counts of units packed into an u64. + */ + u64 (*units)(struct nvkm_gr *); + bool (*chsw_load)(struct nvkm_gr *); + struct { + int (*pause)(struct nvkm_gr *); + int (*resume)(struct nvkm_gr *); + u32 (*inst)(struct nvkm_gr *); + } ctxsw; + struct nvkm_sclass sclass[]; +}; + +extern const struct nvkm_bitfield nv04_gr_nsource[]; +extern const struct nvkm_object_func nv04_gr_object; + +extern const struct nvkm_bitfield nv10_gr_intr_name[]; +extern const struct nvkm_bitfield nv10_gr_nstatus[]; + +extern const struct nvkm_enum nv50_data_error_names[]; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h new file mode 100644 index 000000000..fd1b7d35c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/regs.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_GR_REGS_H__ +#define __NVKM_GR_REGS_H__ + +#define NV04_PGRAPH_DEBUG_0 0x00400080 +#define NV04_PGRAPH_DEBUG_1 0x00400084 +#define NV04_PGRAPH_DEBUG_2 0x00400088 +#define NV04_PGRAPH_DEBUG_3 0x0040008c +#define NV10_PGRAPH_DEBUG_4 0x00400090 +#define NV03_PGRAPH_INTR 0x00400100 +#define NV03_PGRAPH_NSTATUS 0x00400104 +# define NV04_PGRAPH_NSTATUS_STATE_IN_USE (1<<11) +# define NV04_PGRAPH_NSTATUS_INVALID_STATE (1<<12) +# define NV04_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<13) +# define NV04_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<14) +# define NV10_PGRAPH_NSTATUS_STATE_IN_USE (1<<23) +# define NV10_PGRAPH_NSTATUS_INVALID_STATE (1<<24) +# define NV10_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<25) +# define NV10_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<26) +#define NV03_PGRAPH_NSOURCE 0x00400108 +# define NV03_PGRAPH_NSOURCE_NOTIFICATION (1<<0) +# define NV03_PGRAPH_NSOURCE_DATA_ERROR (1<<1) +# define NV03_PGRAPH_NSOURCE_PROTECTION_ERROR (1<<2) +# define NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION (1<<3) +# define NV03_PGRAPH_NSOURCE_LIMIT_COLOR (1<<4) +# define NV03_PGRAPH_NSOURCE_LIMIT_ZETA (1<<5) +# define NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD (1<<6) +# define NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION (1<<7) +# define NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION (1<<8) +# define NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION (1<<9) +# define NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION (1<<10) +# define NV03_PGRAPH_NSOURCE_STATE_INVALID (1<<11) +# define NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY (1<<12) +# define NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE (1<<13) +# define NV03_PGRAPH_NSOURCE_METHOD_CNT (1<<14) +# define NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION (1<<15) +# define NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION (1<<16) +# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_A (1<<17) +# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_B (1<<18) +#define NV03_PGRAPH_INTR_EN 0x00400140 +#define NV40_PGRAPH_INTR_EN 0x0040013C +# define NV_PGRAPH_INTR_NOTIFY (1<<0) +# define NV_PGRAPH_INTR_MISSING_HW (1<<4) +# define NV_PGRAPH_INTR_CONTEXT_SWITCH (1<<12) +# define NV_PGRAPH_INTR_BUFFER_NOTIFY (1<<16) +# define NV_PGRAPH_INTR_ERROR (1<<20) +#define NV10_PGRAPH_CTX_CONTROL 0x00400144 +#define NV10_PGRAPH_CTX_USER 0x00400148 +#define NV10_PGRAPH_CTX_SWITCH(i) (0x0040014C + 0x4*(i)) +#define NV04_PGRAPH_CTX_SWITCH1 0x00400160 +#define NV10_PGRAPH_CTX_CACHE(i, j) (0x00400160 \ + + 0x4*(i) + 0x20*(j)) +#define NV04_PGRAPH_CTX_SWITCH2 0x00400164 +#define NV04_PGRAPH_CTX_SWITCH3 0x00400168 +#define NV04_PGRAPH_CTX_SWITCH4 0x0040016C +#define NV04_PGRAPH_CTX_CONTROL 0x00400170 +#define NV04_PGRAPH_CTX_USER 0x00400174 +#define NV04_PGRAPH_CTX_CACHE1 0x00400180 +#define NV03_PGRAPH_CTX_CONTROL 0x00400190 +#define NV03_PGRAPH_CTX_USER 0x00400194 +#define NV04_PGRAPH_CTX_CACHE2 0x004001A0 +#define NV04_PGRAPH_CTX_CACHE3 0x004001C0 +#define NV04_PGRAPH_CTX_CACHE4 0x004001E0 +#define NV40_PGRAPH_CTXCTL_0304 0x00400304 +#define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001 +#define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308 +#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_MASK 0xff000000 +#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT 24 +#define NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK 0x00ffffff +#define NV40_PGRAPH_CTXCTL_0310 0x00400310 +#define NV40_PGRAPH_CTXCTL_0310_XFER_SAVE 0x00000020 +#define NV40_PGRAPH_CTXCTL_0310_XFER_LOAD 0x00000040 +#define NV40_PGRAPH_CTXCTL_030C 0x0040030c +#define NV40_PGRAPH_CTXCTL_UCODE_INDEX 0x00400324 +#define NV40_PGRAPH_CTXCTL_UCODE_DATA 0x00400328 +#define NV40_PGRAPH_CTXCTL_CUR 0x0040032c +#define NV40_PGRAPH_CTXCTL_CUR_LOADED 0x01000000 +#define NV40_PGRAPH_CTXCTL_CUR_INSTANCE 0x000FFFFF +#define NV40_PGRAPH_CTXCTL_NEXT 0x00400330 +#define NV40_PGRAPH_CTXCTL_NEXT_INSTANCE 0x000fffff +#define NV50_PGRAPH_CTXCTL_CUR 0x0040032c +#define NV50_PGRAPH_CTXCTL_CUR_LOADED 0x80000000 +#define NV50_PGRAPH_CTXCTL_CUR_INSTANCE 0x00ffffff +#define NV50_PGRAPH_CTXCTL_NEXT 0x00400330 +#define NV50_PGRAPH_CTXCTL_NEXT_INSTANCE 0x00ffffff +#define NV03_PGRAPH_ABS_X_RAM 0x00400400 +#define NV03_PGRAPH_ABS_Y_RAM 0x00400480 +#define NV03_PGRAPH_X_MISC 0x00400500 +#define NV03_PGRAPH_Y_MISC 0x00400504 +#define NV04_PGRAPH_VALID1 0x00400508 +#define NV04_PGRAPH_SOURCE_COLOR 0x0040050C +#define NV04_PGRAPH_MISC24_0 0x00400510 +#define NV03_PGRAPH_XY_LOGIC_MISC0 0x00400514 +#define NV03_PGRAPH_XY_LOGIC_MISC1 0x00400518 +#define NV03_PGRAPH_XY_LOGIC_MISC2 0x0040051C +#define NV03_PGRAPH_XY_LOGIC_MISC3 0x00400520 +#define NV03_PGRAPH_CLIPX_0 0x00400524 +#define NV03_PGRAPH_CLIPX_1 0x00400528 +#define NV03_PGRAPH_CLIPY_0 0x0040052C +#define NV03_PGRAPH_CLIPY_1 0x00400530 +#define NV03_PGRAPH_ABS_ICLIP_XMAX 0x00400534 +#define NV03_PGRAPH_ABS_ICLIP_YMAX 0x00400538 +#define NV03_PGRAPH_ABS_UCLIP_XMIN 0x0040053C +#define NV03_PGRAPH_ABS_UCLIP_YMIN 0x00400540 +#define NV03_PGRAPH_ABS_UCLIP_XMAX 0x00400544 +#define NV03_PGRAPH_ABS_UCLIP_YMAX 0x00400548 +#define NV03_PGRAPH_ABS_UCLIPA_XMIN 0x00400560 +#define NV03_PGRAPH_ABS_UCLIPA_YMIN 0x00400564 +#define NV03_PGRAPH_ABS_UCLIPA_XMAX 0x00400568 +#define NV03_PGRAPH_ABS_UCLIPA_YMAX 0x0040056C +#define NV04_PGRAPH_MISC24_1 0x00400570 +#define NV04_PGRAPH_MISC24_2 0x00400574 +#define NV04_PGRAPH_VALID2 0x00400578 +#define NV04_PGRAPH_PASSTHRU_0 0x0040057C +#define NV04_PGRAPH_PASSTHRU_1 0x00400580 +#define NV04_PGRAPH_PASSTHRU_2 0x00400584 +#define NV10_PGRAPH_DIMX_TEXTURE 0x00400588 +#define NV10_PGRAPH_WDIMX_TEXTURE 0x0040058C +#define NV04_PGRAPH_COMBINE_0_ALPHA 0x00400590 +#define NV04_PGRAPH_COMBINE_0_COLOR 0x00400594 +#define NV04_PGRAPH_COMBINE_1_ALPHA 0x00400598 +#define NV04_PGRAPH_COMBINE_1_COLOR 0x0040059C +#define NV04_PGRAPH_FORMAT_0 0x004005A8 +#define NV04_PGRAPH_FORMAT_1 0x004005AC +#define NV04_PGRAPH_FILTER_0 0x004005B0 +#define NV04_PGRAPH_FILTER_1 0x004005B4 +#define NV03_PGRAPH_MONO_COLOR0 0x00400600 +#define NV04_PGRAPH_ROP3 0x00400604 +#define NV04_PGRAPH_BETA_AND 0x00400608 +#define NV04_PGRAPH_BETA_PREMULT 0x0040060C +#define NV04_PGRAPH_LIMIT_VIOL_PIX 0x00400610 +#define NV04_PGRAPH_FORMATS 0x00400618 +#define NV10_PGRAPH_DEBUG_2 0x00400620 +#define NV04_PGRAPH_BOFFSET0 0x00400640 +#define NV04_PGRAPH_BOFFSET1 0x00400644 +#define NV04_PGRAPH_BOFFSET2 0x00400648 +#define NV04_PGRAPH_BOFFSET3 0x0040064C +#define NV04_PGRAPH_BOFFSET4 0x00400650 +#define NV04_PGRAPH_BOFFSET5 0x00400654 +#define NV04_PGRAPH_BBASE0 0x00400658 +#define NV04_PGRAPH_BBASE1 0x0040065C +#define NV04_PGRAPH_BBASE2 0x00400660 +#define NV04_PGRAPH_BBASE3 0x00400664 +#define NV04_PGRAPH_BBASE4 0x00400668 +#define NV04_PGRAPH_BBASE5 0x0040066C +#define NV04_PGRAPH_BPITCH0 0x00400670 +#define NV04_PGRAPH_BPITCH1 0x00400674 +#define NV04_PGRAPH_BPITCH2 0x00400678 +#define NV04_PGRAPH_BPITCH3 0x0040067C +#define NV04_PGRAPH_BPITCH4 0x00400680 +#define NV04_PGRAPH_BLIMIT0 0x00400684 +#define NV04_PGRAPH_BLIMIT1 0x00400688 +#define NV04_PGRAPH_BLIMIT2 0x0040068C +#define NV04_PGRAPH_BLIMIT3 0x00400690 +#define NV04_PGRAPH_BLIMIT4 0x00400694 +#define NV04_PGRAPH_BLIMIT5 0x00400698 +#define NV04_PGRAPH_BSWIZZLE2 0x0040069C +#define NV04_PGRAPH_BSWIZZLE5 0x004006A0 +#define NV03_PGRAPH_STATUS 0x004006B0 +#define NV04_PGRAPH_STATUS 0x00400700 +# define NV40_PGRAPH_STATUS_SYNC_STALL 0x00004000 +#define NV04_PGRAPH_TRAPPED_ADDR 0x00400704 +#define NV04_PGRAPH_TRAPPED_DATA 0x00400708 +#define NV04_PGRAPH_SURFACE 0x0040070C +#define NV10_PGRAPH_TRAPPED_DATA_HIGH 0x0040070C +#define NV04_PGRAPH_STATE 0x00400710 +#define NV10_PGRAPH_SURFACE 0x00400710 +#define NV04_PGRAPH_NOTIFY 0x00400714 +#define NV10_PGRAPH_STATE 0x00400714 +#define NV10_PGRAPH_NOTIFY 0x00400718 + +#define NV04_PGRAPH_FIFO 0x00400720 + +#define NV04_PGRAPH_BPIXEL 0x00400724 +#define NV10_PGRAPH_RDI_INDEX 0x00400750 +#define NV04_PGRAPH_FFINTFC_ST2 0x00400754 +#define NV10_PGRAPH_RDI_DATA 0x00400754 +#define NV04_PGRAPH_DMA_PITCH 0x00400760 +#define NV10_PGRAPH_FFINTFC_FIFO_PTR 0x00400760 +#define NV04_PGRAPH_DVD_COLORFMT 0x00400764 +#define NV10_PGRAPH_FFINTFC_ST2 0x00400764 +#define NV04_PGRAPH_SCALED_FORMAT 0x00400768 +#define NV10_PGRAPH_FFINTFC_ST2_DL 0x00400768 +#define NV10_PGRAPH_FFINTFC_ST2_DH 0x0040076c +#define NV10_PGRAPH_DMA_PITCH 0x00400770 +#define NV10_PGRAPH_DVD_COLORFMT 0x00400774 +#define NV10_PGRAPH_SCALED_FORMAT 0x00400778 +#define NV20_PGRAPH_CHANNEL_CTX_TABLE 0x00400780 +#define NV20_PGRAPH_CHANNEL_CTX_POINTER 0x00400784 +#define NV20_PGRAPH_CHANNEL_CTX_XFER 0x00400788 +#define NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD 0x00000001 +#define NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE 0x00000002 +#define NV04_PGRAPH_PATT_COLOR0 0x00400800 +#define NV04_PGRAPH_PATT_COLOR1 0x00400804 +#define NV04_PGRAPH_PATTERN 0x00400808 +#define NV04_PGRAPH_PATTERN_SHAPE 0x00400810 +#define NV04_PGRAPH_CHROMA 0x00400814 +#define NV04_PGRAPH_CONTROL0 0x00400818 +#define NV04_PGRAPH_CONTROL1 0x0040081C +#define NV04_PGRAPH_CONTROL2 0x00400820 +#define NV04_PGRAPH_BLEND 0x00400824 +#define NV04_PGRAPH_STORED_FMT 0x00400830 +#define NV04_PGRAPH_PATT_COLORRAM 0x00400900 +#define NV20_PGRAPH_TILE(i) (0x00400900 + (i*16)) +#define NV20_PGRAPH_TLIMIT(i) (0x00400904 + (i*16)) +#define NV20_PGRAPH_TSIZE(i) (0x00400908 + (i*16)) +#define NV20_PGRAPH_TSTATUS(i) (0x0040090C + (i*16)) +#define NV20_PGRAPH_ZCOMP(i) (0x00400980 + 4*(i)) +#define NV41_PGRAPH_ZCOMP0(i) (0x004009c0 + 4*(i)) +#define NV10_PGRAPH_TILE(i) (0x00400B00 + (i*16)) +#define NV10_PGRAPH_TLIMIT(i) (0x00400B04 + (i*16)) +#define NV10_PGRAPH_TSIZE(i) (0x00400B08 + (i*16)) +#define NV10_PGRAPH_TSTATUS(i) (0x00400B0C + (i*16)) +#define NV04_PGRAPH_U_RAM 0x00400D00 +#define NV47_PGRAPH_TILE(i) (0x00400D00 + (i*16)) +#define NV47_PGRAPH_TLIMIT(i) (0x00400D04 + (i*16)) +#define NV47_PGRAPH_TSIZE(i) (0x00400D08 + (i*16)) +#define NV47_PGRAPH_TSTATUS(i) (0x00400D0C + (i*16)) +#define NV04_PGRAPH_V_RAM 0x00400D40 +#define NV04_PGRAPH_W_RAM 0x00400D80 +#define NV47_PGRAPH_ZCOMP0(i) (0x00400e00 + 4*(i)) +#define NV10_PGRAPH_COMBINER0_IN_ALPHA 0x00400E40 +#define NV10_PGRAPH_COMBINER1_IN_ALPHA 0x00400E44 +#define NV10_PGRAPH_COMBINER0_IN_RGB 0x00400E48 +#define NV10_PGRAPH_COMBINER1_IN_RGB 0x00400E4C +#define NV10_PGRAPH_COMBINER_COLOR0 0x00400E50 +#define NV10_PGRAPH_COMBINER_COLOR1 0x00400E54 +#define NV10_PGRAPH_COMBINER0_OUT_ALPHA 0x00400E58 +#define NV10_PGRAPH_COMBINER1_OUT_ALPHA 0x00400E5C +#define NV10_PGRAPH_COMBINER0_OUT_RGB 0x00400E60 +#define NV10_PGRAPH_COMBINER1_OUT_RGB 0x00400E64 +#define NV10_PGRAPH_COMBINER_FINAL0 0x00400E68 +#define NV10_PGRAPH_COMBINER_FINAL1 0x00400E6C +#define NV10_PGRAPH_WINDOWCLIP_HORIZONTAL 0x00400F00 +#define NV10_PGRAPH_WINDOWCLIP_VERTICAL 0x00400F20 +#define NV10_PGRAPH_XFMODE0 0x00400F40 +#define NV10_PGRAPH_XFMODE1 0x00400F44 +#define NV10_PGRAPH_GLOBALSTATE0 0x00400F48 +#define NV10_PGRAPH_GLOBALSTATE1 0x00400F4C +#define NV10_PGRAPH_PIPE_ADDRESS 0x00400F50 +#define NV10_PGRAPH_PIPE_DATA 0x00400F54 +#define NV04_PGRAPH_DMA_START_0 0x00401000 +#define NV04_PGRAPH_DMA_START_1 0x00401004 +#define NV04_PGRAPH_DMA_LENGTH 0x00401008 +#define NV04_PGRAPH_DMA_MISC 0x0040100C +#define NV04_PGRAPH_DMA_DATA_0 0x00401020 +#define NV04_PGRAPH_DMA_DATA_1 0x00401024 +#define NV04_PGRAPH_DMA_RM 0x00401030 +#define NV04_PGRAPH_DMA_A_XLATE_INST 0x00401040 +#define NV04_PGRAPH_DMA_A_CONTROL 0x00401044 +#define NV04_PGRAPH_DMA_A_LIMIT 0x00401048 +#define NV04_PGRAPH_DMA_A_TLB_PTE 0x0040104C +#define NV04_PGRAPH_DMA_A_TLB_TAG 0x00401050 +#define NV04_PGRAPH_DMA_A_ADJ_OFFSET 0x00401054 +#define NV04_PGRAPH_DMA_A_OFFSET 0x00401058 +#define NV04_PGRAPH_DMA_A_SIZE 0x0040105C +#define NV04_PGRAPH_DMA_A_Y_SIZE 0x00401060 +#define NV04_PGRAPH_DMA_B_XLATE_INST 0x00401080 +#define NV04_PGRAPH_DMA_B_CONTROL 0x00401084 +#define NV04_PGRAPH_DMA_B_LIMIT 0x00401088 +#define NV04_PGRAPH_DMA_B_TLB_PTE 0x0040108C +#define NV04_PGRAPH_DMA_B_TLB_TAG 0x00401090 +#define NV04_PGRAPH_DMA_B_ADJ_OFFSET 0x00401094 +#define NV04_PGRAPH_DMA_B_OFFSET 0x00401098 +#define NV04_PGRAPH_DMA_B_SIZE 0x0040109C +#define NV04_PGRAPH_DMA_B_Y_SIZE 0x004010A0 +#define NV47_PGRAPH_ZCOMP1(i) (0x004068c0 + 4*(i)) +#define NV40_PGRAPH_TILE1(i) (0x00406900 + (i*16)) +#define NV40_PGRAPH_TLIMIT1(i) (0x00406904 + (i*16)) +#define NV40_PGRAPH_TSIZE1(i) (0x00406908 + (i*16)) +#define NV40_PGRAPH_TSTATUS1(i) (0x0040690C + (i*16)) +#define NV40_PGRAPH_ZCOMP1(i) (0x00406980 + 4*(i)) +#define NV41_PGRAPH_ZCOMP1(i) (0x004069c0 + 4*(i)) + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c new file mode 100644 index 000000000..1a8a21844 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c @@ -0,0 +1,204 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ctxgf100.h" + +#include + +static void +tu102_gr_init_fecs_exceptions(struct gf100_gr *gr) +{ + nvkm_wr32(gr->base.engine.subdev.device, 0x409c24, 0x006f0002); +} + +static void +tu102_gr_init_fs(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + int sm; + + gp100_grctx_generate_smid_config(gr); + gk104_grctx_generate_gpc_tpc_nr(gr); + + for (sm = 0; sm < gr->sm_nr; sm++) { + nvkm_wr32(device, GPC_UNIT(gr->sm[sm].gpc, 0x0c10 + + gr->sm[sm].tpc * 4), sm); + } + + gm200_grctx_generate_dist_skip_table(gr); + gf100_gr_init_num_tpc_per_gpc(gr, true, true); +} + +static void +tu102_gr_init_zcull(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, gr->tpc_total); + const u8 tile_nr = ALIGN(gr->tpc_total, 64); + u8 bank[GPC_MAX] = {}, gpc, i, j; + u32 data; + + for (i = 0; i < tile_nr; i += 8) { + for (data = 0, j = 0; j < 8 && i + j < gr->tpc_total; j++) { + data |= bank[gr->tile[i + j]] << (j * 4); + bank[gr->tile[i + j]]++; + } + nvkm_wr32(device, GPC_BCAST(0x0980 + ((i / 8) * 4)), data); + } + + for (gpc = 0; gpc < gr->gpc_nr; gpc++) { + nvkm_wr32(device, GPC_UNIT(gpc, 0x0914), + gr->screen_tile_row_offset << 8 | gr->tpc_nr[gpc]); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0910), 0x00040000 | + gr->tpc_total); + nvkm_wr32(device, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nvkm_wr32(device, GPC_BCAST(0x3fd4), magicgpc918); +} + +static void +tu102_gr_init_gpc_mmu(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + nvkm_wr32(device, 0x418880, nvkm_rd32(device, 0x100c80) & 0xf8001fff); + nvkm_wr32(device, 0x418890, 0x00000000); + nvkm_wr32(device, 0x418894, 0x00000000); + + nvkm_wr32(device, 0x4188b4, nvkm_rd32(device, 0x100cc8)); + nvkm_wr32(device, 0x4188b8, nvkm_rd32(device, 0x100ccc)); + nvkm_wr32(device, 0x4188b0, nvkm_rd32(device, 0x100cc4)); +} + +static const struct gf100_gr_func +tu102_gr = { + .oneinit_tiles = gm200_gr_oneinit_tiles, + .oneinit_sm_id = gm200_gr_oneinit_sm_id, + .init = gf100_gr_init, + .init_419bd8 = gv100_gr_init_419bd8, + .init_gpc_mmu = tu102_gr_init_gpc_mmu, + .init_vsc_stream_master = gk104_gr_init_vsc_stream_master, + .init_zcull = tu102_gr_init_zcull, + .init_num_active_ltcs = gf100_gr_init_num_active_ltcs, + .init_rop_active_fbps = gp100_gr_init_rop_active_fbps, + .init_swdx_pes_mask = gp102_gr_init_swdx_pes_mask, + .init_fs = tu102_gr_init_fs, + .init_fecs_exceptions = tu102_gr_init_fecs_exceptions, + .init_ds_hww_esr_2 = gm200_gr_init_ds_hww_esr_2, + .init_sked_hww_esr = gk104_gr_init_sked_hww_esr, + .init_ppc_exceptions = gk104_gr_init_ppc_exceptions, + .init_504430 = gv100_gr_init_504430, + .init_shader_exceptions = gv100_gr_init_shader_exceptions, + .trap_mp = gv100_gr_trap_mp, + .rops = gm200_gr_rops, + .gpc_nr = 6, + .tpc_nr = 5, + .ppc_nr = 3, + .grctx = &tu102_grctx, + .zbc = &gp102_gr_zbc, + .sclass = { + { -1, -1, FERMI_TWOD_A }, + { -1, -1, KEPLER_INLINE_TO_MEMORY_B }, + { -1, -1, TURING_A, &gf100_fermi }, + { -1, -1, TURING_COMPUTE_A }, + {} + } +}; + +MODULE_FIRMWARE("nvidia/tu102/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/tu102/gr/sw_method_init.bin"); + +MODULE_FIRMWARE("nvidia/tu104/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/tu104/gr/sw_method_init.bin"); + +MODULE_FIRMWARE("nvidia/tu106/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/tu106/gr/sw_method_init.bin"); + +MODULE_FIRMWARE("nvidia/tu117/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/tu117/gr/sw_method_init.bin"); + +MODULE_FIRMWARE("nvidia/tu116/gr/fecs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/fecs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/fecs_data.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/fecs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/gpccs_bl.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/gpccs_inst.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/gpccs_data.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/gpccs_sig.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/sw_ctx.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/sw_nonctx.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/sw_bundle_init.bin"); +MODULE_FIRMWARE("nvidia/tu116/gr/sw_method_init.bin"); + +static const struct gf100_gr_fwif +tu102_gr_fwif[] = { + { 0, gm200_gr_load, &tu102_gr, &gp108_gr_fecs_acr, &gp108_gr_gpccs_acr }, + { -1, gm200_gr_nofw }, + {} +}; + +int +tu102_gr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_gr **pgr) +{ + return gf100_gr_new_(tu102_gr_fwif, device, type, inst, pgr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild new file mode 100644 index 000000000..8d2d9eae5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/Kbuild @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/mpeg/nv31.o +nvkm-y += nvkm/engine/mpeg/nv40.o +nvkm-y += nvkm/engine/mpeg/nv44.o +nvkm-y += nvkm/engine/mpeg/nv50.o +nvkm-y += nvkm/engine/mpeg/g84.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c new file mode 100644 index 000000000..0fcc0ffa1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/g84.c @@ -0,0 +1,44 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +static const struct nvkm_engine_func +g84_mpeg = { + .init = nv50_mpeg_init, + .intr = nv50_mpeg_intr, + .cclass = &nv50_mpeg_cclass, + .sclass = { + { -1, -1, G82_MPEG, &nv31_mpeg_object }, + {} + } +}; + +int +g84_mpeg_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pmpeg) +{ + return nvkm_engine_new_(&g84_mpeg, device, type, inst, true, pmpeg); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c new file mode 100644 index 000000000..b1054db4c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c @@ -0,0 +1,299 @@ +/* + * 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 "nv31.h" + +#include +#include +#include +#include +#include + +#include + +/******************************************************************************* + * MPEG object classes + ******************************************************************************/ + +static int +nv31_mpeg_object_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + int ret = nvkm_gpuobj_new(object->engine->subdev.device, 16, align, + false, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x00, object->oclass); + nvkm_wo32(*pgpuobj, 0x04, 0x00000000); + nvkm_wo32(*pgpuobj, 0x08, 0x00000000); + nvkm_wo32(*pgpuobj, 0x0c, 0x00000000); + nvkm_done(*pgpuobj); + } + return ret; +} + +const struct nvkm_object_func +nv31_mpeg_object = { + .bind = nv31_mpeg_object_bind, +}; + +/******************************************************************************* + * PMPEG context + ******************************************************************************/ + +static void * +nv31_mpeg_chan_dtor(struct nvkm_object *object) +{ + struct nv31_mpeg_chan *chan = nv31_mpeg_chan(object); + struct nv31_mpeg *mpeg = chan->mpeg; + unsigned long flags; + + spin_lock_irqsave(&mpeg->engine.lock, flags); + if (mpeg->chan == chan) + mpeg->chan = NULL; + spin_unlock_irqrestore(&mpeg->engine.lock, flags); + return chan; +} + +static const struct nvkm_object_func +nv31_mpeg_chan = { + .dtor = nv31_mpeg_chan_dtor, +}; + +int +nv31_mpeg_chan_new(struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct nv31_mpeg *mpeg = nv31_mpeg(oclass->engine); + struct nv31_mpeg_chan *chan; + unsigned long flags; + int ret = -EBUSY; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv31_mpeg_chan, oclass, &chan->object); + chan->mpeg = mpeg; + chan->fifo = fifoch; + *pobject = &chan->object; + + spin_lock_irqsave(&mpeg->engine.lock, flags); + if (!mpeg->chan) { + mpeg->chan = chan; + ret = 0; + } + spin_unlock_irqrestore(&mpeg->engine.lock, flags); + return ret; +} + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +void +nv31_mpeg_tile(struct nvkm_engine *engine, int i, struct nvkm_fb_tile *tile) +{ + struct nv31_mpeg *mpeg = nv31_mpeg(engine); + struct nvkm_device *device = mpeg->engine.subdev.device; + + nvkm_wr32(device, 0x00b008 + (i * 0x10), tile->pitch); + nvkm_wr32(device, 0x00b004 + (i * 0x10), tile->limit); + nvkm_wr32(device, 0x00b000 + (i * 0x10), tile->addr); +} + +static bool +nv31_mpeg_mthd_dma(struct nvkm_device *device, u32 mthd, u32 data) +{ + struct nv31_mpeg *mpeg = nv31_mpeg(device->mpeg); + struct nvkm_subdev *subdev = &mpeg->engine.subdev; + u32 inst = data << 4; + u32 dma0 = nvkm_rd32(device, 0x700000 + inst); + u32 dma1 = nvkm_rd32(device, 0x700004 + inst); + u32 dma2 = nvkm_rd32(device, 0x700008 + inst); + u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); + u32 size = dma1 + 1; + + /* only allow linear DMA objects */ + if (!(dma0 & 0x00002000)) { + nvkm_error(subdev, "inst %08x dma0 %08x dma1 %08x dma2 %08x\n", + inst, dma0, dma1, dma2); + return false; + } + + if (mthd == 0x0190) { + /* DMA_CMD */ + nvkm_mask(device, 0x00b300, 0x00010000, + (dma0 & 0x00030000) ? 0x00010000 : 0); + nvkm_wr32(device, 0x00b334, base); + nvkm_wr32(device, 0x00b324, size); + } else + if (mthd == 0x01a0) { + /* DMA_DATA */ + nvkm_mask(device, 0x00b300, 0x00020000, + (dma0 & 0x00030000) ? 0x00020000 : 0); + nvkm_wr32(device, 0x00b360, base); + nvkm_wr32(device, 0x00b364, size); + } else { + /* DMA_IMAGE, VRAM only */ + if (dma0 & 0x00030000) + return false; + + nvkm_wr32(device, 0x00b370, base); + nvkm_wr32(device, 0x00b374, size); + } + + return true; +} + +static bool +nv31_mpeg_mthd(struct nv31_mpeg *mpeg, u32 mthd, u32 data) +{ + struct nvkm_device *device = mpeg->engine.subdev.device; + switch (mthd) { + case 0x190: + case 0x1a0: + case 0x1b0: + return mpeg->func->mthd_dma(device, mthd, data); + default: + break; + } + return false; +} + +static void +nv31_mpeg_intr(struct nvkm_engine *engine) +{ + struct nv31_mpeg *mpeg = nv31_mpeg(engine); + struct nvkm_subdev *subdev = &mpeg->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x00b100); + u32 type = nvkm_rd32(device, 0x00b230); + u32 mthd = nvkm_rd32(device, 0x00b234); + u32 data = nvkm_rd32(device, 0x00b238); + u32 show = stat; + unsigned long flags; + + spin_lock_irqsave(&mpeg->engine.lock, flags); + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nvkm_mask(device, 0x00b308, 0x00000000, 0x00000000); + show &= ~0x01000000; + } + + if (type == 0x00000010) { + if (nv31_mpeg_mthd(mpeg, mthd, data)) + show &= ~0x01000000; + } + } + + nvkm_wr32(device, 0x00b100, stat); + nvkm_wr32(device, 0x00b230, 0x00000001); + + if (show) { + nvkm_error(subdev, "ch %d [%s] %08x %08x %08x %08x\n", + mpeg->chan ? mpeg->chan->fifo->chid : -1, + mpeg->chan ? mpeg->chan->object.client->name : + "unknown", stat, type, mthd, data); + } + + spin_unlock_irqrestore(&mpeg->engine.lock, flags); +} + +int +nv31_mpeg_init(struct nvkm_engine *mpeg) +{ + struct nvkm_subdev *subdev = &mpeg->subdev; + struct nvkm_device *device = subdev->device; + + /* VPE init */ + nvkm_wr32(device, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + nvkm_wr32(device, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + + /* PMPEG init */ + nvkm_wr32(device, 0x00b32c, 0x00000000); + nvkm_wr32(device, 0x00b314, 0x00000100); + nvkm_wr32(device, 0x00b220, 0x00000031); + nvkm_wr32(device, 0x00b300, 0x02001ec1); + nvkm_mask(device, 0x00b32c, 0x00000001, 0x00000001); + + nvkm_wr32(device, 0x00b100, 0xffffffff); + nvkm_wr32(device, 0x00b140, 0xffffffff); + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x00b200) & 0x00000001)) + break; + ) < 0) { + nvkm_error(subdev, "timeout %08x\n", + nvkm_rd32(device, 0x00b200)); + return -EBUSY; + } + + return 0; +} + +static void * +nv31_mpeg_dtor(struct nvkm_engine *engine) +{ + return nv31_mpeg(engine); +} + +static const struct nvkm_engine_func +nv31_mpeg_ = { + .dtor = nv31_mpeg_dtor, + .init = nv31_mpeg_init, + .intr = nv31_mpeg_intr, + .tile = nv31_mpeg_tile, + .fifo.cclass = nv31_mpeg_chan_new, + .sclass = { + { -1, -1, NV31_MPEG, &nv31_mpeg_object }, + {} + } +}; + +int +nv31_mpeg_new_(const struct nv31_mpeg_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_engine **pmpeg) +{ + struct nv31_mpeg *mpeg; + + if (!(mpeg = kzalloc(sizeof(*mpeg), GFP_KERNEL))) + return -ENOMEM; + mpeg->func = func; + *pmpeg = &mpeg->engine; + + return nvkm_engine_ctor(&nv31_mpeg_, device, type, inst, true, &mpeg->engine); +} + +static const struct nv31_mpeg_func +nv31_mpeg = { + .mthd_dma = nv31_mpeg_mthd_dma, +}; + +int +nv31_mpeg_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pmpeg) +{ + return nv31_mpeg_new_(&nv31_mpeg, device, type, inst, pmpeg); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h new file mode 100644 index 000000000..9f30aaaf8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV31_MPEG_H__ +#define __NV31_MPEG_H__ +#define nv31_mpeg(p) container_of((p), struct nv31_mpeg, engine) +#include "priv.h" +#include + +struct nv31_mpeg { + const struct nv31_mpeg_func *func; + struct nvkm_engine engine; + struct nv31_mpeg_chan *chan; +}; + +int nv31_mpeg_new_(const struct nv31_mpeg_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_engine **); + +struct nv31_mpeg_func { + bool (*mthd_dma)(struct nvkm_device *, u32 mthd, u32 data); +}; + +#define nv31_mpeg_chan(p) container_of((p), struct nv31_mpeg_chan, object) +#include + +struct nv31_mpeg_chan { + struct nvkm_object object; + struct nv31_mpeg *mpeg; + struct nvkm_fifo_chan *fifo; +}; + +int nv31_mpeg_chan_new(struct nvkm_fifo_chan *, const struct nvkm_oclass *, + struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c new file mode 100644 index 000000000..179167484 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv40.c @@ -0,0 +1,83 @@ +/* + * 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 "nv31.h" + +#include + +#include + +bool +nv40_mpeg_mthd_dma(struct nvkm_device *device, u32 mthd, u32 data) +{ + struct nvkm_instmem *imem = device->imem; + struct nv31_mpeg *mpeg = nv31_mpeg(device->mpeg); + struct nvkm_subdev *subdev = &mpeg->engine.subdev; + u32 inst = data << 4; + u32 dma0 = nvkm_instmem_rd32(imem, inst + 0); + u32 dma1 = nvkm_instmem_rd32(imem, inst + 4); + u32 dma2 = nvkm_instmem_rd32(imem, inst + 8); + u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); + u32 size = dma1 + 1; + + /* only allow linear DMA objects */ + if (!(dma0 & 0x00002000)) { + nvkm_error(subdev, "inst %08x dma0 %08x dma1 %08x dma2 %08x\n", + inst, dma0, dma1, dma2); + return false; + } + + if (mthd == 0x0190) { + /* DMA_CMD */ + nvkm_mask(device, 0x00b300, 0x00030000, (dma0 & 0x00030000)); + nvkm_wr32(device, 0x00b334, base); + nvkm_wr32(device, 0x00b324, size); + } else + if (mthd == 0x01a0) { + /* DMA_DATA */ + nvkm_mask(device, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); + nvkm_wr32(device, 0x00b360, base); + nvkm_wr32(device, 0x00b364, size); + } else { + /* DMA_IMAGE, VRAM only */ + if (dma0 & 0x00030000) + return false; + + nvkm_wr32(device, 0x00b370, base); + nvkm_wr32(device, 0x00b374, size); + } + + return true; +} + +static const struct nv31_mpeg_func +nv40_mpeg = { + .mthd_dma = nv40_mpeg_mthd_dma, +}; + +int +nv40_mpeg_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pmpeg) +{ + return nv31_mpeg_new_(&nv40_mpeg, device, type, inst, pmpeg); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c new file mode 100644 index 000000000..521ce43a2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c @@ -0,0 +1,217 @@ +/* + * 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 + */ +#define nv44_mpeg(p) container_of((p), struct nv44_mpeg, engine) +#include "priv.h" + +#include +#include +#include + +#include + +struct nv44_mpeg { + struct nvkm_engine engine; + struct list_head chan; +}; + +/******************************************************************************* + * PMPEG context + ******************************************************************************/ +#define nv44_mpeg_chan(p) container_of((p), struct nv44_mpeg_chan, object) + +struct nv44_mpeg_chan { + struct nvkm_object object; + struct nv44_mpeg *mpeg; + struct nvkm_fifo_chan *fifo; + struct list_head head; + u32 inst; +}; + +static int +nv44_mpeg_chan_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + struct nv44_mpeg_chan *chan = nv44_mpeg_chan(object); + int ret = nvkm_gpuobj_new(chan->object.engine->subdev.device, 264 * 4, + align, true, parent, pgpuobj); + if (ret == 0) { + chan->inst = (*pgpuobj)->addr; + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x78, 0x02001ec1); + nvkm_done(*pgpuobj); + } + return ret; +} + +static int +nv44_mpeg_chan_fini(struct nvkm_object *object, bool suspend) +{ + + struct nv44_mpeg_chan *chan = nv44_mpeg_chan(object); + struct nv44_mpeg *mpeg = chan->mpeg; + struct nvkm_device *device = mpeg->engine.subdev.device; + u32 inst = 0x80000000 | (chan->inst >> 4); + + nvkm_mask(device, 0x00b32c, 0x00000001, 0x00000000); + if (nvkm_rd32(device, 0x00b318) == inst) + nvkm_mask(device, 0x00b318, 0x80000000, 0x00000000); + nvkm_mask(device, 0x00b32c, 0x00000001, 0x00000001); + return 0; +} + +static void * +nv44_mpeg_chan_dtor(struct nvkm_object *object) +{ + struct nv44_mpeg_chan *chan = nv44_mpeg_chan(object); + struct nv44_mpeg *mpeg = chan->mpeg; + unsigned long flags; + spin_lock_irqsave(&mpeg->engine.lock, flags); + list_del(&chan->head); + spin_unlock_irqrestore(&mpeg->engine.lock, flags); + return chan; +} + +static const struct nvkm_object_func +nv44_mpeg_chan = { + .dtor = nv44_mpeg_chan_dtor, + .fini = nv44_mpeg_chan_fini, + .bind = nv44_mpeg_chan_bind, +}; + +static int +nv44_mpeg_chan_new(struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct nv44_mpeg *mpeg = nv44_mpeg(oclass->engine); + struct nv44_mpeg_chan *chan; + unsigned long flags; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nv44_mpeg_chan, oclass, &chan->object); + chan->mpeg = mpeg; + chan->fifo = fifoch; + *pobject = &chan->object; + + spin_lock_irqsave(&mpeg->engine.lock, flags); + list_add(&chan->head, &mpeg->chan); + spin_unlock_irqrestore(&mpeg->engine.lock, flags); + return 0; +} + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +static bool +nv44_mpeg_mthd(struct nvkm_device *device, u32 mthd, u32 data) +{ + switch (mthd) { + case 0x190: + case 0x1a0: + case 0x1b0: + return nv40_mpeg_mthd_dma(device, mthd, data); + default: + break; + } + return false; +} + +static void +nv44_mpeg_intr(struct nvkm_engine *engine) +{ + struct nv44_mpeg *mpeg = nv44_mpeg(engine); + struct nvkm_subdev *subdev = &mpeg->engine.subdev; + struct nvkm_device *device = subdev->device; + struct nv44_mpeg_chan *temp, *chan = NULL; + unsigned long flags; + u32 inst = nvkm_rd32(device, 0x00b318) & 0x000fffff; + u32 stat = nvkm_rd32(device, 0x00b100); + u32 type = nvkm_rd32(device, 0x00b230); + u32 mthd = nvkm_rd32(device, 0x00b234); + u32 data = nvkm_rd32(device, 0x00b238); + u32 show = stat; + + spin_lock_irqsave(&mpeg->engine.lock, flags); + list_for_each_entry(temp, &mpeg->chan, head) { + if (temp->inst >> 4 == inst) { + chan = temp; + list_del(&chan->head); + list_add(&chan->head, &mpeg->chan); + break; + } + } + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nvkm_mask(device, 0x00b308, 0x00000000, 0x00000000); + show &= ~0x01000000; + } + + if (type == 0x00000010) { + if (nv44_mpeg_mthd(subdev->device, mthd, data)) + show &= ~0x01000000; + } + } + + nvkm_wr32(device, 0x00b100, stat); + nvkm_wr32(device, 0x00b230, 0x00000001); + + if (show) { + nvkm_error(subdev, "ch %d [%08x %s] %08x %08x %08x %08x\n", + chan ? chan->fifo->chid : -1, inst << 4, + chan ? chan->object.client->name : "unknown", + stat, type, mthd, data); + } + + spin_unlock_irqrestore(&mpeg->engine.lock, flags); +} + +static const struct nvkm_engine_func +nv44_mpeg = { + .init = nv31_mpeg_init, + .intr = nv44_mpeg_intr, + .tile = nv31_mpeg_tile, + .fifo.cclass = nv44_mpeg_chan_new, + .sclass = { + { -1, -1, NV31_MPEG, &nv31_mpeg_object }, + {} + } +}; + +int +nv44_mpeg_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pmpeg) +{ + struct nv44_mpeg *mpeg; + + if (!(mpeg = kzalloc(sizeof(*mpeg), GFP_KERNEL))) + return -ENOMEM; + INIT_LIST_HEAD(&mpeg->chan); + *pmpeg = &mpeg->engine; + + return nvkm_engine_ctor(&nv44_mpeg, device, type, inst, true, &mpeg->engine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c new file mode 100644 index 000000000..e6374f369 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv50.c @@ -0,0 +1,136 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include +#include + +#include + +/******************************************************************************* + * PMPEG context + ******************************************************************************/ + +static int +nv50_mpeg_cclass_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + int ret = nvkm_gpuobj_new(object->engine->subdev.device, 128 * 4, + align, true, parent, pgpuobj); + if (ret == 0) { + nvkm_kmap(*pgpuobj); + nvkm_wo32(*pgpuobj, 0x70, 0x00801ec1); + nvkm_wo32(*pgpuobj, 0x7c, 0x0000037c); + nvkm_done(*pgpuobj); + } + return ret; +} + +const struct nvkm_object_func +nv50_mpeg_cclass = { + .bind = nv50_mpeg_cclass_bind, +}; + +/******************************************************************************* + * PMPEG engine/subdev functions + ******************************************************************************/ + +void +nv50_mpeg_intr(struct nvkm_engine *mpeg) +{ + struct nvkm_subdev *subdev = &mpeg->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x00b100); + u32 type = nvkm_rd32(device, 0x00b230); + u32 mthd = nvkm_rd32(device, 0x00b234); + u32 data = nvkm_rd32(device, 0x00b238); + u32 show = stat; + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nvkm_wr32(device, 0x00b308, 0x00000100); + show &= ~0x01000000; + } + } + + if (show) { + nvkm_info(subdev, "%08x %08x %08x %08x\n", + stat, type, mthd, data); + } + + nvkm_wr32(device, 0x00b100, stat); + nvkm_wr32(device, 0x00b230, 0x00000001); +} + +int +nv50_mpeg_init(struct nvkm_engine *mpeg) +{ + struct nvkm_subdev *subdev = &mpeg->subdev; + struct nvkm_device *device = subdev->device; + + nvkm_wr32(device, 0x00b32c, 0x00000000); + nvkm_wr32(device, 0x00b314, 0x00000100); + nvkm_wr32(device, 0x00b0e0, 0x0000001a); + + nvkm_wr32(device, 0x00b220, 0x00000044); + nvkm_wr32(device, 0x00b300, 0x00801ec1); + nvkm_wr32(device, 0x00b390, 0x00000000); + nvkm_wr32(device, 0x00b394, 0x00000000); + nvkm_wr32(device, 0x00b398, 0x00000000); + nvkm_mask(device, 0x00b32c, 0x00000001, 0x00000001); + + nvkm_wr32(device, 0x00b100, 0xffffffff); + nvkm_wr32(device, 0x00b140, 0xffffffff); + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x00b200) & 0x00000001)) + break; + ) < 0) { + nvkm_error(subdev, "timeout %08x\n", + nvkm_rd32(device, 0x00b200)); + return -EBUSY; + } + + return 0; +} + +static const struct nvkm_engine_func +nv50_mpeg = { + .init = nv50_mpeg_init, + .intr = nv50_mpeg_intr, + .cclass = &nv50_mpeg_cclass, + .sclass = { + { -1, -1, NV31_MPEG, &nv31_mpeg_object }, + {} + } +}; + +int +nv50_mpeg_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pmpeg) +{ + return nvkm_engine_new_(&nv50_mpeg, device, type, inst, true, pmpeg); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h new file mode 100644 index 000000000..667a2d05d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_MPEG_PRIV_H__ +#define __NVKM_MPEG_PRIV_H__ +#include +struct nvkm_fifo_chan; + +int nv31_mpeg_init(struct nvkm_engine *); +void nv31_mpeg_tile(struct nvkm_engine *, int, struct nvkm_fb_tile *); +extern const struct nvkm_object_func nv31_mpeg_object; + +bool nv40_mpeg_mthd_dma(struct nvkm_device *, u32, u32); + +int nv50_mpeg_init(struct nvkm_engine *); +void nv50_mpeg_intr(struct nvkm_engine *); + +extern const struct nvkm_object_func nv50_mpeg_cclass; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msenc/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/msenc/Kbuild new file mode 100644 index 000000000..4bf033ff4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msenc/Kbuild @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: MIT +#nvkm-y += nvkm/engine/msenc/base.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild new file mode 100644 index 000000000..0cd957d72 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/Kbuild @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/mspdec/base.o +nvkm-y += nvkm/engine/mspdec/g98.o +nvkm-y += nvkm/engine/mspdec/gt215.o +nvkm-y += nvkm/engine/mspdec/gf100.o +nvkm-y += nvkm/engine/mspdec/gk104.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/base.c new file mode 100644 index 000000000..842fcfbd2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/base.c @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +int +nvkm_mspdec_new_(const struct nvkm_falcon_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_engine **pengine) +{ + return nvkm_falcon_new_(func, device, type, inst, true, 0x085000, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c new file mode 100644 index 000000000..ecb06d68f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/g98.c @@ -0,0 +1,50 @@ +/* + * 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, Maarten Lankhorst, Ilia Mirkin + */ +#include "priv.h" + +#include + +void +g98_mspdec_init(struct nvkm_falcon *mspdec) +{ + struct nvkm_device *device = mspdec->engine.subdev.device; + nvkm_wr32(device, 0x085010, 0x0000ffd2); + nvkm_wr32(device, 0x08501c, 0x0000fff2); +} + +static const struct nvkm_falcon_func +g98_mspdec = { + .init = g98_mspdec_init, + .sclass = { + { -1, -1, G98_MSPDEC }, + {} + } +}; + +int +g98_mspdec_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_mspdec_new_(&g98_mspdec, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c new file mode 100644 index 000000000..0a69bd767 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gf100.c @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Maarten Lankhorst + * + * 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: Maarten Lankhorst + */ +#include "priv.h" + +#include + +void +gf100_mspdec_init(struct nvkm_falcon *mspdec) +{ + struct nvkm_device *device = mspdec->engine.subdev.device; + nvkm_wr32(device, 0x085010, 0x0000fff2); + nvkm_wr32(device, 0x08501c, 0x0000fff2); +} + +static const struct nvkm_falcon_func +gf100_mspdec = { + .init = gf100_mspdec_init, + .sclass = { + { -1, -1, GF100_MSPDEC }, + {} + } +}; + +int +gf100_mspdec_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_mspdec_new_(&gf100_mspdec, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c new file mode 100644 index 000000000..a08991dca --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gk104.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +static const struct nvkm_falcon_func +gk104_mspdec = { + .init = gf100_mspdec_init, + .sclass = { + { -1, -1, GK104_MSPDEC }, + {} + } +}; + +int +gk104_mspdec_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_mspdec_new_(&gk104_mspdec, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gt215.c new file mode 100644 index 000000000..791fb03a3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/gt215.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs, Maarten Lankhorst, Ilia Mirkin + */ +#include "priv.h" + +#include + +static const struct nvkm_falcon_func +gt215_mspdec = { + .init = g98_mspdec_init, + .sclass = { + { -1, -1, GT212_MSPDEC }, + {} + } +}; + +int +gt215_mspdec_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_mspdec_new_(>215_mspdec, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/priv.h new file mode 100644 index 000000000..2bc5537d4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mspdec/priv.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_MSPDEC_PRIV_H__ +#define __NVKM_MSPDEC_PRIV_H__ +#include + +int nvkm_mspdec_new_(const struct nvkm_falcon_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_engine **); + +void g98_mspdec_init(struct nvkm_falcon *); + +void gf100_mspdec_init(struct nvkm_falcon *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild new file mode 100644 index 000000000..788ce7255 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/Kbuild @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/msppp/base.o +nvkm-y += nvkm/engine/msppp/g98.o +nvkm-y += nvkm/engine/msppp/gt215.o +nvkm-y += nvkm/engine/msppp/gf100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msppp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/base.c new file mode 100644 index 000000000..45a9411ab --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/base.c @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +int +nvkm_msppp_new_(const struct nvkm_falcon_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_engine **pengine) +{ + return nvkm_falcon_new_(func, device, type, inst, true, 0x086000, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c new file mode 100644 index 000000000..160120b9b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/g98.c @@ -0,0 +1,50 @@ +/* + * 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, Maarten Lankhorst, Ilia Mirkin + */ +#include "priv.h" + +#include + +void +g98_msppp_init(struct nvkm_falcon *msppp) +{ + struct nvkm_device *device = msppp->engine.subdev.device; + nvkm_wr32(device, 0x086010, 0x0000ffd2); + nvkm_wr32(device, 0x08601c, 0x0000fff2); +} + +static const struct nvkm_falcon_func +g98_msppp = { + .init = g98_msppp_init, + .sclass = { + { -1, -1, G98_MSPPP }, + {} + } +}; + +int +g98_msppp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msppp_new_(&g98_msppp, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c new file mode 100644 index 000000000..debed9ae8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gf100.c @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Maarten Lankhorst + * + * 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: Maarten Lankhorst + */ +#include "priv.h" + +#include + +static void +gf100_msppp_init(struct nvkm_falcon *msppp) +{ + struct nvkm_device *device = msppp->engine.subdev.device; + nvkm_wr32(device, 0x086010, 0x0000fff2); + nvkm_wr32(device, 0x08601c, 0x0000fff2); +} + +static const struct nvkm_falcon_func +gf100_msppp = { + .init = gf100_msppp_init, + .sclass = { + { -1, -1, GF100_MSPPP }, + {} + } +}; + +int +gf100_msppp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msppp_new_(&gf100_msppp, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gt215.c new file mode 100644 index 000000000..a2fd736fe --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/gt215.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs, Maarten Lankhorst, Ilia Mirkin + */ +#include "priv.h" + +#include + +static const struct nvkm_falcon_func +gt215_msppp = { + .init = g98_msppp_init, + .sclass = { + { -1, -1, GT212_MSPPP }, + {} + } +}; + +int +gt215_msppp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msppp_new_(>215_msppp, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msppp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/priv.h new file mode 100644 index 000000000..582ab8ce1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msppp/priv.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_MSPPP_PRIV_H__ +#define __NVKM_MSPPP_PRIV_H__ +#include + +int nvkm_msppp_new_(const struct nvkm_falcon_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_engine **); + +void g98_msppp_init(struct nvkm_falcon *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild new file mode 100644 index 000000000..d68b4431c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/Kbuild @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/msvld/base.o +nvkm-y += nvkm/engine/msvld/g98.o +nvkm-y += nvkm/engine/msvld/gt215.o +nvkm-y += nvkm/engine/msvld/mcp89.o +nvkm-y += nvkm/engine/msvld/gf100.o +nvkm-y += nvkm/engine/msvld/gk104.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/base.c new file mode 100644 index 000000000..7be42b980 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/base.c @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +int +nvkm_msvld_new_(const struct nvkm_falcon_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_engine **pengine) +{ + return nvkm_falcon_new_(func, device, type, inst, true, 0x084000, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c new file mode 100644 index 000000000..cfa206531 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/g98.c @@ -0,0 +1,50 @@ +/* + * 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, Maarten Lankhorst, Ilia Mirkin + */ +#include "priv.h" + +#include + +void +g98_msvld_init(struct nvkm_falcon *msvld) +{ + struct nvkm_device *device = msvld->engine.subdev.device; + nvkm_wr32(device, 0x084010, 0x0000ffd2); + nvkm_wr32(device, 0x08401c, 0x0000fff2); +} + +static const struct nvkm_falcon_func +g98_msvld = { + .init = g98_msvld_init, + .sclass = { + { -1, -1, G98_MSVLD }, + {} + } +}; + +int +g98_msvld_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msvld_new_(&g98_msvld, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c new file mode 100644 index 000000000..8d58ad8e0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gf100.c @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Maarten Lankhorst + * + * 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: Maarten Lankhorst + */ +#include "priv.h" + +#include + +void +gf100_msvld_init(struct nvkm_falcon *msvld) +{ + struct nvkm_device *device = msvld->engine.subdev.device; + nvkm_wr32(device, 0x084010, 0x0000fff2); + nvkm_wr32(device, 0x08401c, 0x0000fff2); +} + +static const struct nvkm_falcon_func +gf100_msvld = { + .init = gf100_msvld_init, + .sclass = { + { -1, -1, GF100_MSVLD }, + {} + } +}; + +int +gf100_msvld_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msvld_new_(&gf100_msvld, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c new file mode 100644 index 000000000..b28be2804 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gk104.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +static const struct nvkm_falcon_func +gk104_msvld = { + .init = gf100_msvld_init, + .sclass = { + { -1, -1, GK104_MSVLD }, + {} + } +}; + +int +gk104_msvld_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msvld_new_(&gk104_msvld, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gt215.c new file mode 100644 index 000000000..d7489f972 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/gt215.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs, Maarten Lankhorst, Ilia Mirkin + */ +#include "priv.h" + +#include + +static const struct nvkm_falcon_func +gt215_msvld = { + .init = g98_msvld_init, + .sclass = { + { -1, -1, GT212_MSVLD }, + {} + } +}; + +int +gt215_msvld_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msvld_new_(>215_msvld, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/mcp89.c new file mode 100644 index 000000000..16c30b62a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/mcp89.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs, Maarten Lankhorst, Ilia Mirkin + */ +#include "priv.h" + +#include + +static const struct nvkm_falcon_func +mcp89_msvld = { + .init = g98_msvld_init, + .sclass = { + { -1, -1, IGT21A_MSVLD }, + {} + } +}; + +int +mcp89_msvld_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_msvld_new_(&mcp89_msvld, device, type, inst, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/msvld/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/priv.h new file mode 100644 index 000000000..f729d919b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/msvld/priv.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_MSVLD_PRIV_H__ +#define __NVKM_MSVLD_PRIV_H__ +#include + +int nvkm_msvld_new_(const struct nvkm_falcon_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_engine **); + +void g98_msvld_init(struct nvkm_falcon *); + +void gf100_msvld_init(struct nvkm_falcon *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild new file mode 100644 index 000000000..9a0fd9812 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/nvdec/base.o +nvkm-y += nvkm/engine/nvdec/gm107.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c new file mode 100644 index 000000000..b0181cc59 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include + +static void * +nvkm_nvdec_dtor(struct nvkm_engine *engine) +{ + struct nvkm_nvdec *nvdec = nvkm_nvdec(engine); + nvkm_falcon_dtor(&nvdec->falcon); + return nvdec; +} + +static const struct nvkm_engine_func +nvkm_nvdec = { + .dtor = nvkm_nvdec_dtor, +}; + +int +nvkm_nvdec_new_(const struct nvkm_nvdec_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_nvdec **pnvdec) +{ + struct nvkm_nvdec *nvdec; + int ret; + + if (!(nvdec = *pnvdec = kzalloc(sizeof(*nvdec), GFP_KERNEL))) + return -ENOMEM; + + ret = nvkm_engine_ctor(&nvkm_nvdec, device, type, inst, true, + &nvdec->engine); + if (ret) + return ret; + + fwif = nvkm_firmware_load(&nvdec->engine.subdev, fwif, "Nvdec", nvdec); + if (IS_ERR(fwif)) + return -ENODEV; + + nvdec->func = fwif->func; + + return nvkm_falcon_ctor(nvdec->func->flcn, &nvdec->engine.subdev, + nvdec->engine.subdev.name, 0, &nvdec->falcon); +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gm107.c new file mode 100644 index 000000000..8c44ce44a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gm107.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +static const struct nvkm_falcon_func +gm107_nvdec_flcn = { + .debug = 0xd00, + .fbif = 0x600, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .bind_context = nvkm_falcon_v1_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = nvkm_falcon_v1_enable, + .disable = nvkm_falcon_v1_disable, +}; + +static const struct nvkm_nvdec_func +gm107_nvdec = { + .flcn = &gm107_nvdec_flcn, +}; + +static int +gm107_nvdec_nofw(struct nvkm_nvdec *nvdec, int ver, + const struct nvkm_nvdec_fwif *fwif) +{ + return 0; +} + +static const struct nvkm_nvdec_fwif +gm107_nvdec_fwif[] = { + { -1, gm107_nvdec_nofw, &gm107_nvdec }, + {} +}; + +int +gm107_nvdec_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_nvdec **pnvdec) +{ + return nvkm_nvdec_new_(gm107_nvdec_fwif, device, type, inst, pnvdec); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h new file mode 100644 index 000000000..0920f6a88 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_NVDEC_PRIV_H__ +#define __NVKM_NVDEC_PRIV_H__ +#include + +struct nvkm_nvdec_func { + const struct nvkm_falcon_func *flcn; +}; + +struct nvkm_nvdec_fwif { + int version; + int (*load)(struct nvkm_nvdec *, int ver, + const struct nvkm_nvdec_fwif *); + const struct nvkm_nvdec_func *func; +}; + +int nvkm_nvdec_new_(const struct nvkm_nvdec_fwif *fwif, struct nvkm_device *, + enum nvkm_subdev_type, int, struct nvkm_nvdec **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/Kbuild new file mode 100644 index 000000000..75bf4436b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/Kbuild @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/nvenc/base.o +nvkm-y += nvkm/engine/nvenc/gm107.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c new file mode 100644 index 000000000..cf5dcfda7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include + +static void * +nvkm_nvenc_dtor(struct nvkm_engine *engine) +{ + struct nvkm_nvenc *nvenc = nvkm_nvenc(engine); + nvkm_falcon_dtor(&nvenc->falcon); + return nvenc; +} + +static const struct nvkm_engine_func +nvkm_nvenc = { + .dtor = nvkm_nvenc_dtor, +}; + +int +nvkm_nvenc_new_(const struct nvkm_nvenc_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_nvenc **pnvenc) +{ + struct nvkm_nvenc *nvenc; + int ret; + + if (!(nvenc = *pnvenc = kzalloc(sizeof(*nvenc), GFP_KERNEL))) + return -ENOMEM; + + ret = nvkm_engine_ctor(&nvkm_nvenc, device, type, inst, true, + &nvenc->engine); + if (ret) + return ret; + + fwif = nvkm_firmware_load(&nvenc->engine.subdev, fwif, "Nvenc", nvenc); + if (IS_ERR(fwif)) + return -ENODEV; + + nvenc->func = fwif->func; + + return nvkm_falcon_ctor(nvenc->func->flcn, &nvenc->engine.subdev, + nvenc->engine.subdev.name, 0, &nvenc->falcon); +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/gm107.c new file mode 100644 index 000000000..f44d41bf2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/gm107.c @@ -0,0 +1,63 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "priv.h" + +static const struct nvkm_falcon_func +gm107_nvenc_flcn = { + .fbif = 0x800, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .bind_context = nvkm_falcon_v1_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = nvkm_falcon_v1_enable, + .disable = nvkm_falcon_v1_disable, +}; + +static const struct nvkm_nvenc_func +gm107_nvenc = { + .flcn = &gm107_nvenc_flcn, +}; + +static int +gm107_nvenc_nofw(struct nvkm_nvenc *nvenc, int ver, + const struct nvkm_nvenc_fwif *fwif) +{ + return 0; +} + +static const struct nvkm_nvenc_fwif +gm107_nvenc_fwif[] = { + { -1, gm107_nvenc_nofw, &gm107_nvenc }, + {} +}; + +int +gm107_nvenc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_nvenc **pnvenc) +{ + return nvkm_nvenc_new_(gm107_nvenc_fwif, device, type, inst, pnvenc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/priv.h new file mode 100644 index 000000000..4130a2bfb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/priv.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_NVENC_PRIV_H__ +#define __NVKM_NVENC_PRIV_H__ +#include + +struct nvkm_nvenc_func { + const struct nvkm_falcon_func *flcn; +}; + +struct nvkm_nvenc_fwif { + int version; + int (*load)(struct nvkm_nvenc *, int ver, + const struct nvkm_nvenc_fwif *); + const struct nvkm_nvenc_func *func; +}; + +int nvkm_nvenc_new_(const struct nvkm_nvenc_fwif *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_nvenc **pnvenc); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild new file mode 100644 index 000000000..2cc8a5f6f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/pm/base.o +nvkm-y += nvkm/engine/pm/nv40.o +nvkm-y += nvkm/engine/pm/nv50.o +nvkm-y += nvkm/engine/pm/g84.o +nvkm-y += nvkm/engine/pm/gt200.o +nvkm-y += nvkm/engine/pm/gt215.o +nvkm-y += nvkm/engine/pm/gf100.o +nvkm-y += nvkm/engine/pm/gf108.o +nvkm-y += nvkm/engine/pm/gf117.o +nvkm-y += nvkm/engine/pm/gk104.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c new file mode 100644 index 000000000..8fe0444f7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c @@ -0,0 +1,868 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +#include +#include +#include +#include +#include + +static u8 +nvkm_pm_count_perfdom(struct nvkm_pm *pm) +{ + struct nvkm_perfdom *dom; + u8 domain_nr = 0; + + list_for_each_entry(dom, &pm->domains, head) + domain_nr++; + return domain_nr; +} + +static u16 +nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom) +{ + u16 signal_nr = 0; + int i; + + if (dom) { + for (i = 0; i < dom->signal_nr; i++) { + if (dom->signal[i].name) + signal_nr++; + } + } + return signal_nr; +} + +static struct nvkm_perfdom * +nvkm_perfdom_find(struct nvkm_pm *pm, int di) +{ + struct nvkm_perfdom *dom; + int tmp = 0; + + list_for_each_entry(dom, &pm->domains, head) { + if (tmp++ == di) + return dom; + } + return NULL; +} + +static struct nvkm_perfsig * +nvkm_perfsig_find(struct nvkm_pm *pm, u8 di, u8 si, struct nvkm_perfdom **pdom) +{ + struct nvkm_perfdom *dom = *pdom; + + if (dom == NULL) { + dom = nvkm_perfdom_find(pm, di); + if (dom == NULL) + return NULL; + *pdom = dom; + } + + if (!dom->signal[si].name) + return NULL; + return &dom->signal[si]; +} + +static u8 +nvkm_perfsig_count_perfsrc(struct nvkm_perfsig *sig) +{ + u8 source_nr = 0, i; + + for (i = 0; i < ARRAY_SIZE(sig->source); i++) { + if (sig->source[i]) + source_nr++; + } + return source_nr; +} + +static struct nvkm_perfsrc * +nvkm_perfsrc_find(struct nvkm_pm *pm, struct nvkm_perfsig *sig, int si) +{ + struct nvkm_perfsrc *src; + bool found = false; + int tmp = 1; /* Sources ID start from 1 */ + u8 i; + + for (i = 0; i < ARRAY_SIZE(sig->source) && sig->source[i]; i++) { + if (sig->source[i] == si) { + found = true; + break; + } + } + + if (found) { + list_for_each_entry(src, &pm->sources, head) { + if (tmp++ == si) + return src; + } + } + + return NULL; +} + +static int +nvkm_perfsrc_enable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr) +{ + struct nvkm_subdev *subdev = &pm->engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_perfdom *dom = NULL; + struct nvkm_perfsig *sig; + struct nvkm_perfsrc *src; + u32 mask, value; + int i, j; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 8 && ctr->source[i][j]; j++) { + sig = nvkm_perfsig_find(pm, ctr->domain, + ctr->signal[i], &dom); + if (!sig) + return -EINVAL; + + src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]); + if (!src) + return -EINVAL; + + /* set enable bit if needed */ + mask = value = 0x00000000; + if (src->enable) + mask = value = 0x80000000; + mask |= (src->mask << src->shift); + value |= ((ctr->source[i][j] >> 32) << src->shift); + + /* enable the source */ + nvkm_mask(device, src->addr, mask, value); + nvkm_debug(subdev, + "enabled source %08x %08x %08x\n", + src->addr, mask, value); + } + } + return 0; +} + +static int +nvkm_perfsrc_disable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr) +{ + struct nvkm_subdev *subdev = &pm->engine.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_perfdom *dom = NULL; + struct nvkm_perfsig *sig; + struct nvkm_perfsrc *src; + u32 mask; + int i, j; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 8 && ctr->source[i][j]; j++) { + sig = nvkm_perfsig_find(pm, ctr->domain, + ctr->signal[i], &dom); + if (!sig) + return -EINVAL; + + src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]); + if (!src) + return -EINVAL; + + /* unset enable bit if needed */ + mask = 0x00000000; + if (src->enable) + mask = 0x80000000; + mask |= (src->mask << src->shift); + + /* disable the source */ + nvkm_mask(device, src->addr, mask, 0); + nvkm_debug(subdev, "disabled source %08x %08x\n", + src->addr, mask); + } + } + return 0; +} + +/******************************************************************************* + * Perfdom object classes + ******************************************************************************/ +static int +nvkm_perfdom_init(struct nvkm_perfdom *dom, void *data, u32 size) +{ + union { + struct nvif_perfdom_init none; + } *args = data; + struct nvkm_object *object = &dom->object; + struct nvkm_pm *pm = dom->perfmon->pm; + int ret = -ENOSYS, i; + + nvif_ioctl(object, "perfdom init size %d\n", size); + if (!(ret = nvif_unvers(ret, &data, &size, args->none))) { + nvif_ioctl(object, "perfdom init\n"); + } else + return ret; + + for (i = 0; i < 4; i++) { + if (dom->ctr[i]) { + dom->func->init(pm, dom, dom->ctr[i]); + + /* enable sources */ + nvkm_perfsrc_enable(pm, dom->ctr[i]); + } + } + + /* start next batch of counters for sampling */ + dom->func->next(pm, dom); + return 0; +} + +static int +nvkm_perfdom_sample(struct nvkm_perfdom *dom, void *data, u32 size) +{ + union { + struct nvif_perfdom_sample none; + } *args = data; + struct nvkm_object *object = &dom->object; + struct nvkm_pm *pm = dom->perfmon->pm; + int ret = -ENOSYS; + + nvif_ioctl(object, "perfdom sample size %d\n", size); + if (!(ret = nvif_unvers(ret, &data, &size, args->none))) { + nvif_ioctl(object, "perfdom sample\n"); + } else + return ret; + pm->sequence++; + + /* sample previous batch of counters */ + list_for_each_entry(dom, &pm->domains, head) + dom->func->next(pm, dom); + + return 0; +} + +static int +nvkm_perfdom_read(struct nvkm_perfdom *dom, void *data, u32 size) +{ + union { + struct nvif_perfdom_read_v0 v0; + } *args = data; + struct nvkm_object *object = &dom->object; + struct nvkm_pm *pm = dom->perfmon->pm; + int ret = -ENOSYS, i; + + nvif_ioctl(object, "perfdom read size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "perfdom read vers %d\n", args->v0.version); + } else + return ret; + + for (i = 0; i < 4; i++) { + if (dom->ctr[i]) + dom->func->read(pm, dom, dom->ctr[i]); + } + + if (!dom->clk) + return -EAGAIN; + + for (i = 0; i < 4; i++) + if (dom->ctr[i]) + args->v0.ctr[i] = dom->ctr[i]->ctr; + args->v0.clk = dom->clk; + return 0; +} + +static int +nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_perfdom *dom = nvkm_perfdom(object); + switch (mthd) { + case NVIF_PERFDOM_V0_INIT: + return nvkm_perfdom_init(dom, data, size); + case NVIF_PERFDOM_V0_SAMPLE: + return nvkm_perfdom_sample(dom, data, size); + case NVIF_PERFDOM_V0_READ: + return nvkm_perfdom_read(dom, data, size); + default: + break; + } + return -EINVAL; +} + +static void * +nvkm_perfdom_dtor(struct nvkm_object *object) +{ + struct nvkm_perfdom *dom = nvkm_perfdom(object); + struct nvkm_pm *pm = dom->perfmon->pm; + int i; + + for (i = 0; i < 4; i++) { + struct nvkm_perfctr *ctr = dom->ctr[i]; + if (ctr) { + nvkm_perfsrc_disable(pm, ctr); + if (ctr->head.next) + list_del(&ctr->head); + } + kfree(ctr); + } + + return dom; +} + +static int +nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot, u8 domain, + struct nvkm_perfsig *signal[4], u64 source[4][8], + u16 logic_op, struct nvkm_perfctr **pctr) +{ + struct nvkm_perfctr *ctr; + int i, j; + + if (!dom) + return -EINVAL; + + ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL); + if (!ctr) + return -ENOMEM; + + ctr->domain = domain; + ctr->logic_op = logic_op; + ctr->slot = slot; + for (i = 0; i < 4; i++) { + if (signal[i]) { + ctr->signal[i] = signal[i] - dom->signal; + for (j = 0; j < 8; j++) + ctr->source[i][j] = source[i][j]; + } + } + list_add_tail(&ctr->head, &dom->list); + + return 0; +} + +static const struct nvkm_object_func +nvkm_perfdom = { + .dtor = nvkm_perfdom_dtor, + .mthd = nvkm_perfdom_mthd, +}; + +static int +nvkm_perfdom_new_(struct nvkm_perfmon *perfmon, + const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + union { + struct nvif_perfdom_v0 v0; + } *args = data; + struct nvkm_pm *pm = perfmon->pm; + struct nvkm_object *parent = oclass->parent; + struct nvkm_perfdom *sdom = NULL; + struct nvkm_perfctr *ctr[4] = {}; + struct nvkm_perfdom *dom; + int c, s, m; + int ret = -ENOSYS; + + nvif_ioctl(parent, "create perfdom size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n", + args->v0.version, args->v0.domain, args->v0.mode); + } else + return ret; + + for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) { + struct nvkm_perfsig *sig[4] = {}; + u64 src[4][8] = {}; + + for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) { + sig[s] = nvkm_perfsig_find(pm, args->v0.domain, + args->v0.ctr[c].signal[s], + &sdom); + if (args->v0.ctr[c].signal[s] && !sig[s]) + return -EINVAL; + + for (m = 0; m < 8; m++) { + src[s][m] = args->v0.ctr[c].source[s][m]; + if (src[s][m] && !nvkm_perfsrc_find(pm, sig[s], + src[s][m])) + return -EINVAL; + } + } + + ret = nvkm_perfctr_new(sdom, c, args->v0.domain, sig, src, + args->v0.ctr[c].logic_op, &ctr[c]); + if (ret) + return ret; + } + + if (!sdom) + return -EINVAL; + + if (!(dom = kzalloc(sizeof(*dom), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nvkm_perfdom, oclass, &dom->object); + dom->perfmon = perfmon; + *pobject = &dom->object; + + dom->func = sdom->func; + dom->addr = sdom->addr; + dom->mode = args->v0.mode; + for (c = 0; c < ARRAY_SIZE(ctr); c++) + dom->ctr[c] = ctr[c]; + return 0; +} + +/******************************************************************************* + * Perfmon object classes + ******************************************************************************/ +static int +nvkm_perfmon_mthd_query_domain(struct nvkm_perfmon *perfmon, + void *data, u32 size) +{ + union { + struct nvif_perfmon_query_domain_v0 v0; + } *args = data; + struct nvkm_object *object = &perfmon->object; + struct nvkm_pm *pm = perfmon->pm; + struct nvkm_perfdom *dom; + u8 domain_nr; + int di, ret = -ENOSYS; + + nvif_ioctl(object, "perfmon query domain size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "perfmon domain vers %d iter %02x\n", + args->v0.version, args->v0.iter); + di = (args->v0.iter & 0xff) - 1; + } else + return ret; + + domain_nr = nvkm_pm_count_perfdom(pm); + if (di >= (int)domain_nr) + return -EINVAL; + + if (di >= 0) { + dom = nvkm_perfdom_find(pm, di); + if (dom == NULL) + return -EINVAL; + + args->v0.id = di; + args->v0.signal_nr = nvkm_perfdom_count_perfsig(dom); + strncpy(args->v0.name, dom->name, sizeof(args->v0.name) - 1); + + /* Currently only global counters (PCOUNTER) are implemented + * but this will be different for local counters (MP). */ + args->v0.counter_nr = 4; + } + + if (++di < domain_nr) { + args->v0.iter = ++di; + return 0; + } + + args->v0.iter = 0xff; + return 0; +} + +static int +nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon, + void *data, u32 size) +{ + union { + struct nvif_perfmon_query_signal_v0 v0; + } *args = data; + struct nvkm_object *object = &perfmon->object; + struct nvkm_pm *pm = perfmon->pm; + struct nvkm_device *device = pm->engine.subdev.device; + struct nvkm_perfdom *dom; + struct nvkm_perfsig *sig; + const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false); + const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all); + int ret = -ENOSYS, si; + + nvif_ioctl(object, "perfmon query signal size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, + "perfmon query signal vers %d dom %d iter %04x\n", + args->v0.version, args->v0.domain, args->v0.iter); + si = (args->v0.iter & 0xffff) - 1; + } else + return ret; + + dom = nvkm_perfdom_find(pm, args->v0.domain); + if (dom == NULL || si >= (int)dom->signal_nr) + return -EINVAL; + + if (si >= 0) { + sig = &dom->signal[si]; + if (raw || !sig->name) { + snprintf(args->v0.name, sizeof(args->v0.name), + "/%s/%02x", dom->name, si); + } else { + strncpy(args->v0.name, sig->name, + sizeof(args->v0.name) - 1); + } + + args->v0.signal = si; + args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig); + } + + while (++si < dom->signal_nr) { + if (all || dom->signal[si].name) { + args->v0.iter = ++si; + return 0; + } + } + + args->v0.iter = 0xffff; + return 0; +} + +static int +nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon, + void *data, u32 size) +{ + union { + struct nvif_perfmon_query_source_v0 v0; + } *args = data; + struct nvkm_object *object = &perfmon->object; + struct nvkm_pm *pm = perfmon->pm; + struct nvkm_perfdom *dom = NULL; + struct nvkm_perfsig *sig; + struct nvkm_perfsrc *src; + u8 source_nr = 0; + int si, ret = -ENOSYS; + + nvif_ioctl(object, "perfmon query source size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, + "perfmon source vers %d dom %d sig %02x iter %02x\n", + args->v0.version, args->v0.domain, args->v0.signal, + args->v0.iter); + si = (args->v0.iter & 0xff) - 1; + } else + return ret; + + sig = nvkm_perfsig_find(pm, args->v0.domain, args->v0.signal, &dom); + if (!sig) + return -EINVAL; + + source_nr = nvkm_perfsig_count_perfsrc(sig); + if (si >= (int)source_nr) + return -EINVAL; + + if (si >= 0) { + src = nvkm_perfsrc_find(pm, sig, sig->source[si]); + if (!src) + return -EINVAL; + + args->v0.source = sig->source[si]; + args->v0.mask = src->mask; + strncpy(args->v0.name, src->name, sizeof(args->v0.name) - 1); + } + + if (++si < source_nr) { + args->v0.iter = ++si; + return 0; + } + + args->v0.iter = 0xff; + return 0; +} + +static int +nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_perfmon *perfmon = nvkm_perfmon(object); + switch (mthd) { + case NVIF_PERFMON_V0_QUERY_DOMAIN: + return nvkm_perfmon_mthd_query_domain(perfmon, data, size); + case NVIF_PERFMON_V0_QUERY_SIGNAL: + return nvkm_perfmon_mthd_query_signal(perfmon, data, size); + case NVIF_PERFMON_V0_QUERY_SOURCE: + return nvkm_perfmon_mthd_query_source(perfmon, data, size); + default: + break; + } + return -EINVAL; +} + +static int +nvkm_perfmon_child_new(const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_perfmon *perfmon = nvkm_perfmon(oclass->parent); + return nvkm_perfdom_new_(perfmon, oclass, data, size, pobject); +} + +static int +nvkm_perfmon_child_get(struct nvkm_object *object, int index, + struct nvkm_oclass *oclass) +{ + if (index == 0) { + oclass->base.oclass = NVIF_CLASS_PERFDOM; + oclass->base.minver = 0; + oclass->base.maxver = 0; + oclass->ctor = nvkm_perfmon_child_new; + return 0; + } + return -EINVAL; +} + +static void * +nvkm_perfmon_dtor(struct nvkm_object *object) +{ + struct nvkm_perfmon *perfmon = nvkm_perfmon(object); + struct nvkm_pm *pm = perfmon->pm; + spin_lock(&pm->client.lock); + if (pm->client.object == &perfmon->object) + pm->client.object = NULL; + spin_unlock(&pm->client.lock); + return perfmon; +} + +static const struct nvkm_object_func +nvkm_perfmon = { + .dtor = nvkm_perfmon_dtor, + .mthd = nvkm_perfmon_mthd, + .sclass = nvkm_perfmon_child_get, +}; + +static int +nvkm_perfmon_new(struct nvkm_pm *pm, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_perfmon *perfmon; + + if (!(perfmon = kzalloc(sizeof(*perfmon), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nvkm_perfmon, oclass, &perfmon->object); + perfmon->pm = pm; + *pobject = &perfmon->object; + return 0; +} + +/******************************************************************************* + * PPM engine/subdev functions + ******************************************************************************/ + +static int +nvkm_pm_oclass_new(struct nvkm_device *device, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + struct nvkm_pm *pm = nvkm_pm(oclass->engine); + int ret; + + ret = nvkm_perfmon_new(pm, oclass, data, size, pobject); + if (ret) + return ret; + + spin_lock(&pm->client.lock); + if (pm->client.object == NULL) + pm->client.object = *pobject; + ret = (pm->client.object == *pobject) ? 0 : -EBUSY; + spin_unlock(&pm->client.lock); + return ret; +} + +static const struct nvkm_device_oclass +nvkm_pm_oclass = { + .base.oclass = NVIF_CLASS_PERFMON, + .base.minver = -1, + .base.maxver = -1, + .ctor = nvkm_pm_oclass_new, +}; + +static int +nvkm_pm_oclass_get(struct nvkm_oclass *oclass, int index, + const struct nvkm_device_oclass **class) +{ + if (index == 0) { + oclass->base = nvkm_pm_oclass.base; + *class = &nvkm_pm_oclass; + return index; + } + return 1; +} + +static int +nvkm_perfsrc_new(struct nvkm_pm *pm, struct nvkm_perfsig *sig, + const struct nvkm_specsrc *spec) +{ + const struct nvkm_specsrc *ssrc; + const struct nvkm_specmux *smux; + struct nvkm_perfsrc *src; + u8 source_nr = 0; + + if (!spec) { + /* No sources are defined for this signal. */ + return 0; + } + + ssrc = spec; + while (ssrc->name) { + smux = ssrc->mux; + while (smux->name) { + bool found = false; + u8 source_id = 0; + u32 len; + + list_for_each_entry(src, &pm->sources, head) { + if (src->addr == ssrc->addr && + src->shift == smux->shift) { + found = true; + break; + } + source_id++; + } + + if (!found) { + src = kzalloc(sizeof(*src), GFP_KERNEL); + if (!src) + return -ENOMEM; + + src->addr = ssrc->addr; + src->mask = smux->mask; + src->shift = smux->shift; + src->enable = smux->enable; + + len = strlen(ssrc->name) + + strlen(smux->name) + 2; + src->name = kzalloc(len, GFP_KERNEL); + if (!src->name) { + kfree(src); + return -ENOMEM; + } + snprintf(src->name, len, "%s_%s", ssrc->name, + smux->name); + + list_add_tail(&src->head, &pm->sources); + } + + sig->source[source_nr++] = source_id + 1; + smux++; + } + ssrc++; + } + + return 0; +} + +int +nvkm_perfdom_new(struct nvkm_pm *pm, const char *name, u32 mask, + u32 base, u32 size_unit, u32 size_domain, + const struct nvkm_specdom *spec) +{ + const struct nvkm_specdom *sdom; + const struct nvkm_specsig *ssig; + struct nvkm_perfdom *dom; + int ret, i; + + for (i = 0; i == 0 || mask; i++) { + u32 addr = base + (i * size_unit); + if (i && !(mask & (1 << i))) + continue; + + sdom = spec; + while (sdom->signal_nr) { + dom = kzalloc(struct_size(dom, signal, sdom->signal_nr), + GFP_KERNEL); + if (!dom) + return -ENOMEM; + + if (mask) { + snprintf(dom->name, sizeof(dom->name), + "%s/%02x/%02x", name, i, + (int)(sdom - spec)); + } else { + snprintf(dom->name, sizeof(dom->name), + "%s/%02x", name, (int)(sdom - spec)); + } + + list_add_tail(&dom->head, &pm->domains); + INIT_LIST_HEAD(&dom->list); + dom->func = sdom->func; + dom->addr = addr; + dom->signal_nr = sdom->signal_nr; + + ssig = (sdom++)->signal; + while (ssig->name) { + struct nvkm_perfsig *sig = + &dom->signal[ssig->signal]; + sig->name = ssig->name; + ret = nvkm_perfsrc_new(pm, sig, ssig->source); + if (ret) + return ret; + ssig++; + } + + addr += size_domain; + } + + mask &= ~(1 << i); + } + + return 0; +} + +static int +nvkm_pm_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_pm *pm = nvkm_pm(engine); + if (pm->func->fini) + pm->func->fini(pm); + return 0; +} + +static void * +nvkm_pm_dtor(struct nvkm_engine *engine) +{ + struct nvkm_pm *pm = nvkm_pm(engine); + struct nvkm_perfdom *dom, *next_dom; + struct nvkm_perfsrc *src, *next_src; + + list_for_each_entry_safe(dom, next_dom, &pm->domains, head) { + list_del(&dom->head); + kfree(dom); + } + + list_for_each_entry_safe(src, next_src, &pm->sources, head) { + list_del(&src->head); + kfree(src->name); + kfree(src); + } + + return pm; +} + +static const struct nvkm_engine_func +nvkm_pm = { + .dtor = nvkm_pm_dtor, + .fini = nvkm_pm_fini, + .base.sclass = nvkm_pm_oclass_get, +}; + +int +nvkm_pm_ctor(const struct nvkm_pm_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_pm *pm) +{ + pm->func = func; + INIT_LIST_HEAD(&pm->domains); + INIT_LIST_HEAD(&pm->sources); + spin_lock_init(&pm->client.lock); + return nvkm_engine_ctor(&nvkm_pm, device, type, inst, true, &pm->engine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c new file mode 100644 index 000000000..0086d00eb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c @@ -0,0 +1,165 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +const struct nvkm_specsrc +g84_vfetch_sources[] = { + { 0x400c0c, (const struct nvkm_specmux[]) { + { 0x3, 0, "unk0" }, + {} + }, "pgraph_vfetch_unk0c" }, + {} +}; + +static const struct nvkm_specsrc +g84_prop_sources[] = { + { 0x408e50, (const struct nvkm_specmux[]) { + { 0x1f, 0, "sel", true }, + {} + }, "pgraph_tpc0_prop_pm_mux" }, + {} +}; + +static const struct nvkm_specsrc +g84_crop_sources[] = { + { 0x407008, (const struct nvkm_specmux[]) { + { 0xf, 0, "sel0", true }, + { 0x7, 16, "sel1", true }, + {} + }, "pgraph_rop0_crop_pm_mux" }, + {} +}; + +static const struct nvkm_specsrc +g84_tex_sources[] = { + { 0x408808, (const struct nvkm_specmux[]) { + { 0xfffff, 0, "unk0" }, + {} + }, "pgraph_tpc0_tex_unk08" }, + {} +}; + +static const struct nvkm_specdom +g84_pm[] = { + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0xf0, (const struct nvkm_specsig[]) { + { 0xbd, "pc01_gr_idle" }, + { 0x5e, "pc01_strmout_00" }, + { 0x5f, "pc01_strmout_01" }, + { 0xd2, "pc01_trast_00" }, + { 0xd3, "pc01_trast_01" }, + { 0xd4, "pc01_trast_02" }, + { 0xd5, "pc01_trast_03" }, + { 0xd8, "pc01_trast_04" }, + { 0xd9, "pc01_trast_05" }, + { 0x5c, "pc01_vattr_00" }, + { 0x5d, "pc01_vattr_01" }, + { 0x66, "pc01_vfetch_00", g84_vfetch_sources }, + { 0x67, "pc01_vfetch_01", g84_vfetch_sources }, + { 0x68, "pc01_vfetch_02", g84_vfetch_sources }, + { 0x69, "pc01_vfetch_03", g84_vfetch_sources }, + { 0x6a, "pc01_vfetch_04", g84_vfetch_sources }, + { 0x6b, "pc01_vfetch_05", g84_vfetch_sources }, + { 0x6c, "pc01_vfetch_06", g84_vfetch_sources }, + { 0x6d, "pc01_vfetch_07", g84_vfetch_sources }, + { 0x6e, "pc01_vfetch_08", g84_vfetch_sources }, + { 0x6f, "pc01_vfetch_09", g84_vfetch_sources }, + { 0x70, "pc01_vfetch_0a", g84_vfetch_sources }, + { 0x71, "pc01_vfetch_0b", g84_vfetch_sources }, + { 0x72, "pc01_vfetch_0c", g84_vfetch_sources }, + { 0x73, "pc01_vfetch_0d", g84_vfetch_sources }, + { 0x74, "pc01_vfetch_0e", g84_vfetch_sources }, + { 0x75, "pc01_vfetch_0f", g84_vfetch_sources }, + { 0x76, "pc01_vfetch_10", g84_vfetch_sources }, + { 0x77, "pc01_vfetch_11", g84_vfetch_sources }, + { 0x78, "pc01_vfetch_12", g84_vfetch_sources }, + { 0x79, "pc01_vfetch_13", g84_vfetch_sources }, + { 0x7a, "pc01_vfetch_14", g84_vfetch_sources }, + { 0x7b, "pc01_vfetch_15", g84_vfetch_sources }, + { 0x7c, "pc01_vfetch_16", g84_vfetch_sources }, + { 0x7d, "pc01_vfetch_17", g84_vfetch_sources }, + { 0x7e, "pc01_vfetch_18", g84_vfetch_sources }, + { 0x7f, "pc01_vfetch_19", g84_vfetch_sources }, + { 0x07, "pc01_zcull_00", nv50_zcull_sources }, + { 0x08, "pc01_zcull_01", nv50_zcull_sources }, + { 0x09, "pc01_zcull_02", nv50_zcull_sources }, + { 0x0a, "pc01_zcull_03", nv50_zcull_sources }, + { 0x0b, "pc01_zcull_04", nv50_zcull_sources }, + { 0x0c, "pc01_zcull_05", nv50_zcull_sources }, + { 0xa4, "pc01_unk00" }, + { 0xec, "pc01_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0xa0, (const struct nvkm_specsig[]) { + { 0x30, "pc02_crop_00", g84_crop_sources }, + { 0x31, "pc02_crop_01", g84_crop_sources }, + { 0x32, "pc02_crop_02", g84_crop_sources }, + { 0x33, "pc02_crop_03", g84_crop_sources }, + { 0x00, "pc02_prop_00", g84_prop_sources }, + { 0x01, "pc02_prop_01", g84_prop_sources }, + { 0x02, "pc02_prop_02", g84_prop_sources }, + { 0x03, "pc02_prop_03", g84_prop_sources }, + { 0x04, "pc02_prop_04", g84_prop_sources }, + { 0x05, "pc02_prop_05", g84_prop_sources }, + { 0x06, "pc02_prop_06", g84_prop_sources }, + { 0x07, "pc02_prop_07", g84_prop_sources }, + { 0x48, "pc02_tex_00", g84_tex_sources }, + { 0x49, "pc02_tex_01", g84_tex_sources }, + { 0x4a, "pc02_tex_02", g84_tex_sources }, + { 0x4b, "pc02_tex_03", g84_tex_sources }, + { 0x1a, "pc02_tex_04", g84_tex_sources }, + { 0x1b, "pc02_tex_05", g84_tex_sources }, + { 0x1c, "pc02_tex_06", g84_tex_sources }, + { 0x44, "pc02_zrop_00", nv50_zrop_sources }, + { 0x45, "pc02_zrop_01", nv50_zrop_sources }, + { 0x46, "pc02_zrop_02", nv50_zrop_sources }, + { 0x47, "pc02_zrop_03", nv50_zrop_sources }, + { 0x8c, "pc02_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +int +g84_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return nv40_pm_new_(g84_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c new file mode 100644 index 000000000..8e02701de --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c @@ -0,0 +1,243 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" + +const struct nvkm_specsrc +gf100_pbfb_sources[] = { + { 0x10f100, (const struct nvkm_specmux[]) { + { 0x1, 0, "unk0" }, + { 0x3f, 4, "unk4" }, + {} + }, "pbfb_broadcast_pm_unk100" }, + {} +}; + +const struct nvkm_specsrc +gf100_pmfb_sources[] = { + { 0x140028, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + { 0x7, 16, "unk16" }, + { 0x3, 24, "unk24" }, + { 0x2, 29, "unk29" }, + {} + }, "pmfb0_pm_unk28" }, + {} +}; + +static const struct nvkm_specsrc +gf100_l1_sources[] = { + { 0x5044a8, (const struct nvkm_specmux[]) { + { 0x3f, 0, "sel", true }, + {} + }, "pgraph_gpc0_tpc0_l1_pm_mux" }, + {} +}; + +static const struct nvkm_specsrc +gf100_tex_sources[] = { + { 0x5042c0, (const struct nvkm_specmux[]) { + { 0xf, 0, "sel0", true }, + { 0x7, 8, "sel1", true }, + {} + }, "pgraph_gpc0_tpc0_tex_pm_mux_c_d" }, + {} +}; + +static const struct nvkm_specsrc +gf100_unk400_sources[] = { + { 0x50440c, (const struct nvkm_specmux[]) { + { 0x3f, 0, "sel", true }, + {} + }, "pgraph_gpc0_tpc0_unk400_pm_mux" }, + {} +}; + +static const struct nvkm_specdom +gf100_pm_hub[] = { + {} +}; + +const struct nvkm_specdom +gf100_pm_gpc[] = { + { 0xe0, (const struct nvkm_specsig[]) { + { 0x00, "gpc00_l1_00", gf100_l1_sources }, + { 0x01, "gpc00_l1_01", gf100_l1_sources }, + { 0x02, "gpc00_l1_02", gf100_l1_sources }, + { 0x03, "gpc00_l1_03", gf100_l1_sources }, + { 0x05, "gpc00_l1_04", gf100_l1_sources }, + { 0x06, "gpc00_l1_05", gf100_l1_sources }, + { 0x0a, "gpc00_tex_00", gf100_tex_sources }, + { 0x0b, "gpc00_tex_01", gf100_tex_sources }, + { 0x0c, "gpc00_tex_02", gf100_tex_sources }, + { 0x0d, "gpc00_tex_03", gf100_tex_sources }, + { 0x0e, "gpc00_tex_04", gf100_tex_sources }, + { 0x0f, "gpc00_tex_05", gf100_tex_sources }, + { 0x10, "gpc00_tex_06", gf100_tex_sources }, + { 0x11, "gpc00_tex_07", gf100_tex_sources }, + { 0x12, "gpc00_tex_08", gf100_tex_sources }, + { 0x26, "gpc00_unk400_00", gf100_unk400_sources }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static const struct nvkm_specdom +gf100_pm_part[] = { + { 0xe0, (const struct nvkm_specsig[]) { + { 0x0f, "part00_pbfb_00", gf100_pbfb_sources }, + { 0x10, "part00_pbfb_01", gf100_pbfb_sources }, + { 0x21, "part00_pmfb_00", gf100_pmfb_sources }, + { 0x04, "part00_pmfb_01", gf100_pmfb_sources }, + { 0x00, "part00_pmfb_02", gf100_pmfb_sources }, + { 0x02, "part00_pmfb_03", gf100_pmfb_sources }, + { 0x01, "part00_pmfb_04", gf100_pmfb_sources }, + { 0x2e, "part00_pmfb_05", gf100_pmfb_sources }, + { 0x2f, "part00_pmfb_06", gf100_pmfb_sources }, + { 0x1b, "part00_pmfb_07", gf100_pmfb_sources }, + { 0x1c, "part00_pmfb_08", gf100_pmfb_sources }, + { 0x1d, "part00_pmfb_09", gf100_pmfb_sources }, + { 0x1e, "part00_pmfb_0a", gf100_pmfb_sources }, + { 0x1f, "part00_pmfb_0b", gf100_pmfb_sources }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static void +gf100_perfctr_init(struct nvkm_pm *pm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct nvkm_device *device = pm->engine.subdev.device; + u32 log = ctr->logic_op; + u32 src = 0x00000000; + int i; + + for (i = 0; i < 4; i++) + src |= ctr->signal[i] << (i * 8); + + nvkm_wr32(device, dom->addr + 0x09c, 0x00040002 | (dom->mode << 3)); + nvkm_wr32(device, dom->addr + 0x100, 0x00000000); + nvkm_wr32(device, dom->addr + 0x040 + (ctr->slot * 0x08), src); + nvkm_wr32(device, dom->addr + 0x044 + (ctr->slot * 0x08), log); +} + +static void +gf100_perfctr_read(struct nvkm_pm *pm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct nvkm_device *device = pm->engine.subdev.device; + + switch (ctr->slot) { + case 0: ctr->ctr = nvkm_rd32(device, dom->addr + 0x08c); break; + case 1: ctr->ctr = nvkm_rd32(device, dom->addr + 0x088); break; + case 2: ctr->ctr = nvkm_rd32(device, dom->addr + 0x080); break; + case 3: ctr->ctr = nvkm_rd32(device, dom->addr + 0x090); break; + } + dom->clk = nvkm_rd32(device, dom->addr + 0x070); +} + +static void +gf100_perfctr_next(struct nvkm_pm *pm, struct nvkm_perfdom *dom) +{ + struct nvkm_device *device = pm->engine.subdev.device; + nvkm_wr32(device, dom->addr + 0x06c, dom->signal_nr - 0x40 + 0x27); + nvkm_wr32(device, dom->addr + 0x0ec, 0x00000011); +} + +const struct nvkm_funcdom +gf100_perfctr_func = { + .init = gf100_perfctr_init, + .read = gf100_perfctr_read, + .next = gf100_perfctr_next, +}; + +static void +gf100_pm_fini(struct nvkm_pm *pm) +{ + struct nvkm_device *device = pm->engine.subdev.device; + nvkm_mask(device, 0x000200, 0x10000000, 0x00000000); + nvkm_mask(device, 0x000200, 0x10000000, 0x10000000); +} + +static const struct nvkm_pm_func +gf100_pm_ = { + .fini = gf100_pm_fini, +}; + +int +gf100_pm_new_(const struct gf100_pm_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + struct nvkm_pm *pm; + u32 mask; + int ret; + + if (!(pm = *ppm = kzalloc(sizeof(*pm), GFP_KERNEL))) + return -ENOMEM; + + ret = nvkm_pm_ctor(&gf100_pm_, device, type, inst, pm); + if (ret) + return ret; + + /* HUB */ + ret = nvkm_perfdom_new(pm, "hub", 0, 0x1b0000, 0, 0x200, + func->doms_hub); + if (ret) + return ret; + + /* GPC */ + mask = (1 << nvkm_rd32(device, 0x022430)) - 1; + mask &= ~nvkm_rd32(device, 0x022504); + mask &= ~nvkm_rd32(device, 0x022584); + + ret = nvkm_perfdom_new(pm, "gpc", mask, 0x180000, + 0x1000, 0x200, func->doms_gpc); + if (ret) + return ret; + + /* PART */ + mask = (1 << nvkm_rd32(device, 0x022438)) - 1; + mask &= ~nvkm_rd32(device, 0x022548); + mask &= ~nvkm_rd32(device, 0x0225c8); + + ret = nvkm_perfdom_new(pm, "part", mask, 0x1a0000, + 0x1000, 0x200, func->doms_part); + if (ret) + return ret; + + return 0; +} + +static const struct gf100_pm_func +gf100_pm = { + .doms_gpc = gf100_pm_gpc, + .doms_hub = gf100_pm_hub, + .doms_part = gf100_pm_part, +}; + +int +gf100_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return gf100_pm_new_(&gf100_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h new file mode 100644 index 000000000..bc4b014c4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PM_NVC0_H__ +#define __NVKM_PM_NVC0_H__ +#include "priv.h" + +struct gf100_pm_func { + const struct nvkm_specdom *doms_hub; + const struct nvkm_specdom *doms_gpc; + const struct nvkm_specdom *doms_part; +}; + +int gf100_pm_new_(const struct gf100_pm_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_pm **); + +extern const struct nvkm_funcdom gf100_perfctr_func; +extern const struct nvkm_specdom gf100_pm_gpc[]; + +extern const struct nvkm_specsrc gf100_pbfb_sources[]; +extern const struct nvkm_specsrc gf100_pmfb_sources[]; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf108.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf108.c new file mode 100644 index 000000000..505565866 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf108.c @@ -0,0 +1,66 @@ +/* + * Copyright 2015 Samuel Pitoiset + * + * 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: Samuel Pitoiset + */ +#include "gf100.h" + +static const struct nvkm_specdom +gf108_pm_hub[] = { + {} +}; + +static const struct nvkm_specdom +gf108_pm_part[] = { + { 0xe0, (const struct nvkm_specsig[]) { + { 0x14, "part00_pbfb_00", gf100_pbfb_sources }, + { 0x15, "part00_pbfb_01", gf100_pbfb_sources }, + { 0x20, "part00_pbfb_02", gf100_pbfb_sources }, + { 0x21, "part00_pbfb_03", gf100_pbfb_sources }, + { 0x01, "part00_pmfb_00", gf100_pmfb_sources }, + { 0x04, "part00_pmfb_01", gf100_pmfb_sources }, + { 0x05, "part00_pmfb_02", gf100_pmfb_sources}, + { 0x07, "part00_pmfb_03", gf100_pmfb_sources }, + { 0x0d, "part00_pmfb_04", gf100_pmfb_sources }, + { 0x12, "part00_pmfb_05", gf100_pmfb_sources }, + { 0x13, "part00_pmfb_06", gf100_pmfb_sources }, + { 0x2c, "part00_pmfb_07", gf100_pmfb_sources }, + { 0x2d, "part00_pmfb_08", gf100_pmfb_sources }, + { 0x2e, "part00_pmfb_09", gf100_pmfb_sources }, + { 0x2f, "part00_pmfb_0a", gf100_pmfb_sources }, + { 0x30, "part00_pmfb_0b", gf100_pmfb_sources }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static const struct gf100_pm_func +gf108_pm = { + .doms_gpc = gf100_pm_gpc, + .doms_hub = gf108_pm_hub, + .doms_part = gf108_pm_part, +}; + +int +gf108_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return gf100_pm_new_(&gf108_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf117.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf117.c new file mode 100644 index 000000000..c61e8c010 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gf117.c @@ -0,0 +1,80 @@ +/* + * Copyright 2015 Samuel Pitoiset + * + * 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: Samuel Pitoiset + */ +#include "gf100.h" + +static const struct nvkm_specsrc +gf117_pmfb_sources[] = { + { 0x140028, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + { 0x7, 16, "unk16" }, + { 0x3, 24, "unk24" }, + { 0x2, 28, "unk28" }, + {} + }, "pmfb0_pm_unk28" }, + { 0x14125c, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + {} + }, "pmfb0_subp0_pm_unk25c" }, + {} +}; + +static const struct nvkm_specdom +gf117_pm_hub[] = { + {} +}; + +static const struct nvkm_specdom +gf117_pm_part[] = { + { 0xe0, (const struct nvkm_specsig[]) { + { 0x00, "part00_pbfb_00", gf100_pbfb_sources }, + { 0x01, "part00_pbfb_01", gf100_pbfb_sources }, + { 0x12, "part00_pmfb_00", gf117_pmfb_sources }, + { 0x15, "part00_pmfb_01", gf117_pmfb_sources }, + { 0x16, "part00_pmfb_02", gf117_pmfb_sources }, + { 0x18, "part00_pmfb_03", gf117_pmfb_sources }, + { 0x1e, "part00_pmfb_04", gf117_pmfb_sources }, + { 0x23, "part00_pmfb_05", gf117_pmfb_sources }, + { 0x24, "part00_pmfb_06", gf117_pmfb_sources }, + { 0x0c, "part00_pmfb_07", gf117_pmfb_sources }, + { 0x0d, "part00_pmfb_08", gf117_pmfb_sources }, + { 0x0e, "part00_pmfb_09", gf117_pmfb_sources }, + { 0x0f, "part00_pmfb_0a", gf117_pmfb_sources }, + { 0x10, "part00_pmfb_0b", gf117_pmfb_sources }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static const struct gf100_pm_func +gf117_pm = { + .doms_gpc = gf100_pm_gpc, + .doms_hub = gf117_pm_hub, + .doms_part = gf117_pm_part, +}; + +int +gf117_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return gf100_pm_new_(&gf117_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c new file mode 100644 index 000000000..75bf3df1c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c @@ -0,0 +1,184 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" + +static const struct nvkm_specsrc +gk104_pmfb_sources[] = { + { 0x140028, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + { 0x7, 16, "unk16" }, + { 0x3, 24, "unk24" }, + { 0x2, 28, "unk28" }, + {} + }, "pmfb0_pm_unk28" }, + { 0x14125c, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + {} + }, "pmfb0_subp0_pm_unk25c" }, + { 0x14165c, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + {} + }, "pmfb0_subp1_pm_unk25c" }, + { 0x141a5c, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + {} + }, "pmfb0_subp2_pm_unk25c" }, + { 0x141e5c, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + {} + }, "pmfb0_subp3_pm_unk25c" }, + {} +}; + +static const struct nvkm_specsrc +gk104_tex_sources[] = { + { 0x5042c0, (const struct nvkm_specmux[]) { + { 0xf, 0, "sel0", true }, + { 0x7, 8, "sel1", true }, + {} + }, "pgraph_gpc0_tpc0_tex_pm_mux_c_d" }, + { 0x5042c8, (const struct nvkm_specmux[]) { + { 0x1f, 0, "sel", true }, + {} + }, "pgraph_gpc0_tpc0_tex_pm_unkc8" }, + { 0x5042b8, (const struct nvkm_specmux[]) { + { 0xff, 0, "sel", true }, + {} + }, "pgraph_gpc0_tpc0_tex_pm_unkb8" }, + {} +}; + +static const struct nvkm_specdom +gk104_pm_hub[] = { + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "hub00_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x40, (const struct nvkm_specsig[]) { + { 0x27, "hub01_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "hub02_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "hub03_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x40, (const struct nvkm_specsig[]) { + { 0x03, "host_mmio_rd" }, + { 0x27, "hub04_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "hub05_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0xc0, (const struct nvkm_specsig[]) { + { 0x74, "host_fb_rd3x" }, + { 0x75, "host_fb_rd3x_2" }, + { 0xa7, "hub06_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "hub07_user_0" }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static const struct nvkm_specdom +gk104_pm_gpc[] = { + { 0xe0, (const struct nvkm_specsig[]) { + { 0xc7, "gpc00_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &gf100_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + { 0x00, "gpc02_tex_00", gk104_tex_sources }, + { 0x01, "gpc02_tex_01", gk104_tex_sources }, + { 0x02, "gpc02_tex_02", gk104_tex_sources }, + { 0x03, "gpc02_tex_03", gk104_tex_sources }, + { 0x04, "gpc02_tex_04", gk104_tex_sources }, + { 0x05, "gpc02_tex_05", gk104_tex_sources }, + { 0x06, "gpc02_tex_06", gk104_tex_sources }, + { 0x07, "gpc02_tex_07", gk104_tex_sources }, + { 0x08, "gpc02_tex_08", gk104_tex_sources }, + { 0x0a, "gpc02_tex_0a", gk104_tex_sources }, + { 0x0b, "gpc02_tex_0b", gk104_tex_sources }, + { 0x0d, "gpc02_tex_0c", gk104_tex_sources }, + { 0x0c, "gpc02_tex_0d", gk104_tex_sources }, + { 0x0e, "gpc02_tex_0e", gk104_tex_sources }, + { 0x0f, "gpc02_tex_0f", gk104_tex_sources }, + { 0x10, "gpc02_tex_10", gk104_tex_sources }, + { 0x11, "gpc02_tex_11", gk104_tex_sources }, + { 0x12, "gpc02_tex_12", gk104_tex_sources }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static const struct nvkm_specdom +gk104_pm_part[] = { + { 0x60, (const struct nvkm_specsig[]) { + { 0x00, "part00_pbfb_00", gf100_pbfb_sources }, + { 0x01, "part00_pbfb_01", gf100_pbfb_sources }, + { 0x0c, "part00_pmfb_00", gk104_pmfb_sources }, + { 0x0d, "part00_pmfb_01", gk104_pmfb_sources }, + { 0x0e, "part00_pmfb_02", gk104_pmfb_sources }, + { 0x0f, "part00_pmfb_03", gk104_pmfb_sources }, + { 0x10, "part00_pmfb_04", gk104_pmfb_sources }, + { 0x12, "part00_pmfb_05", gk104_pmfb_sources }, + { 0x15, "part00_pmfb_06", gk104_pmfb_sources }, + { 0x16, "part00_pmfb_07", gk104_pmfb_sources }, + { 0x18, "part00_pmfb_08", gk104_pmfb_sources }, + { 0x21, "part00_pmfb_09", gk104_pmfb_sources }, + { 0x25, "part00_pmfb_0a", gk104_pmfb_sources }, + { 0x26, "part00_pmfb_0b", gk104_pmfb_sources }, + { 0x27, "part00_pmfb_0c", gk104_pmfb_sources }, + { 0x47, "part00_user_0" }, + {} + }, &gf100_perfctr_func }, + { 0x60, (const struct nvkm_specsig[]) { + { 0x47, "part01_user_0" }, + {} + }, &gf100_perfctr_func }, + {} +}; + +static const struct gf100_pm_func +gk104_pm = { + .doms_gpc = gk104_pm_gpc, + .doms_hub = gk104_pm_hub, + .doms_part = gk104_pm_part, +}; + +int +gk104_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return gf100_pm_new_(&gk104_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt200.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt200.c new file mode 100644 index 000000000..25874c541 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt200.c @@ -0,0 +1,157 @@ +/* + * Copyright 2015 Nouveau project + * + * 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: Samuel Pitoiset + */ +#include "nv40.h" + +const struct nvkm_specsrc +gt200_crop_sources[] = { + { 0x407008, (const struct nvkm_specmux[]) { + { 0xf, 0, "sel0", true }, + { 0x1f, 16, "sel1", true }, + {} + }, "pgraph_rop0_crop_pm_mux" }, + {} +}; + +const struct nvkm_specsrc +gt200_prop_sources[] = { + { 0x408750, (const struct nvkm_specmux[]) { + { 0x3f, 0, "sel", true }, + {} + }, "pgraph_tpc0_prop_pm_mux" }, + {} +}; + +const struct nvkm_specsrc +gt200_tex_sources[] = { + { 0x408508, (const struct nvkm_specmux[]) { + { 0xfffff, 0, "unk0" }, + {} + }, "pgraph_tpc0_tex_unk08" }, + {} +}; + +static const struct nvkm_specdom +gt200_pm[] = { + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0xf0, (const struct nvkm_specsig[]) { + { 0xc9, "pc01_gr_idle" }, + { 0x84, "pc01_strmout_00" }, + { 0x85, "pc01_strmout_01" }, + { 0xde, "pc01_trast_00" }, + { 0xdf, "pc01_trast_01" }, + { 0xe0, "pc01_trast_02" }, + { 0xe1, "pc01_trast_03" }, + { 0xe4, "pc01_trast_04" }, + { 0xe5, "pc01_trast_05" }, + { 0x82, "pc01_vattr_00" }, + { 0x83, "pc01_vattr_01" }, + { 0x46, "pc01_vfetch_00", g84_vfetch_sources }, + { 0x47, "pc01_vfetch_01", g84_vfetch_sources }, + { 0x48, "pc01_vfetch_02", g84_vfetch_sources }, + { 0x49, "pc01_vfetch_03", g84_vfetch_sources }, + { 0x4a, "pc01_vfetch_04", g84_vfetch_sources }, + { 0x4b, "pc01_vfetch_05", g84_vfetch_sources }, + { 0x4c, "pc01_vfetch_06", g84_vfetch_sources }, + { 0x4d, "pc01_vfetch_07", g84_vfetch_sources }, + { 0x4e, "pc01_vfetch_08", g84_vfetch_sources }, + { 0x4f, "pc01_vfetch_09", g84_vfetch_sources }, + { 0x50, "pc01_vfetch_0a", g84_vfetch_sources }, + { 0x51, "pc01_vfetch_0b", g84_vfetch_sources }, + { 0x52, "pc01_vfetch_0c", g84_vfetch_sources }, + { 0x53, "pc01_vfetch_0d", g84_vfetch_sources }, + { 0x54, "pc01_vfetch_0e", g84_vfetch_sources }, + { 0x55, "pc01_vfetch_0f", g84_vfetch_sources }, + { 0x56, "pc01_vfetch_10", g84_vfetch_sources }, + { 0x57, "pc01_vfetch_11", g84_vfetch_sources }, + { 0x58, "pc01_vfetch_12", g84_vfetch_sources }, + { 0x59, "pc01_vfetch_13", g84_vfetch_sources }, + { 0x5a, "pc01_vfetch_14", g84_vfetch_sources }, + { 0x5b, "pc01_vfetch_15", g84_vfetch_sources }, + { 0x5c, "pc01_vfetch_16", g84_vfetch_sources }, + { 0x5d, "pc01_vfetch_17", g84_vfetch_sources }, + { 0x5e, "pc01_vfetch_18", g84_vfetch_sources }, + { 0x5f, "pc01_vfetch_19", g84_vfetch_sources }, + { 0x07, "pc01_zcull_00", nv50_zcull_sources }, + { 0x08, "pc01_zcull_01", nv50_zcull_sources }, + { 0x09, "pc01_zcull_02", nv50_zcull_sources }, + { 0x0a, "pc01_zcull_03", nv50_zcull_sources }, + { 0x0b, "pc01_zcull_04", nv50_zcull_sources }, + { 0x0c, "pc01_zcull_05", nv50_zcull_sources }, + + { 0xb0, "pc01_unk00" }, + { 0xec, "pc01_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0xf0, (const struct nvkm_specsig[]) { + { 0x55, "pc02_crop_00", gt200_crop_sources }, + { 0x56, "pc02_crop_01", gt200_crop_sources }, + { 0x57, "pc02_crop_02", gt200_crop_sources }, + { 0x58, "pc02_crop_03", gt200_crop_sources }, + { 0x00, "pc02_prop_00", gt200_prop_sources }, + { 0x01, "pc02_prop_01", gt200_prop_sources }, + { 0x02, "pc02_prop_02", gt200_prop_sources }, + { 0x03, "pc02_prop_03", gt200_prop_sources }, + { 0x04, "pc02_prop_04", gt200_prop_sources }, + { 0x05, "pc02_prop_05", gt200_prop_sources }, + { 0x06, "pc02_prop_06", gt200_prop_sources }, + { 0x07, "pc02_prop_07", gt200_prop_sources }, + { 0x78, "pc02_tex_00", gt200_tex_sources }, + { 0x79, "pc02_tex_01", gt200_tex_sources }, + { 0x7a, "pc02_tex_02", gt200_tex_sources }, + { 0x7b, "pc02_tex_03", gt200_tex_sources }, + { 0x32, "pc02_tex_04", gt200_tex_sources }, + { 0x33, "pc02_tex_05", gt200_tex_sources }, + { 0x34, "pc02_tex_06", gt200_tex_sources }, + { 0x74, "pc02_zrop_00", nv50_zrop_sources }, + { 0x75, "pc02_zrop_01", nv50_zrop_sources }, + { 0x76, "pc02_zrop_02", nv50_zrop_sources }, + { 0x77, "pc02_zrop_03", nv50_zrop_sources }, + { 0xec, "pc02_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +int +gt200_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return nv40_pm_new_(gt200_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c new file mode 100644 index 000000000..54c23e2b6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c @@ -0,0 +1,138 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +static const struct nvkm_specsrc +gt215_zcull_sources[] = { + { 0x402ca4, (const struct nvkm_specmux[]) { + { 0x7fff, 0, "unk0" }, + { 0xff, 24, "unk24" }, + {} + }, "pgraph_zcull_pm_unka4" }, + {} +}; + +static const struct nvkm_specdom +gt215_pm[] = { + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0xf0, (const struct nvkm_specsig[]) { + { 0xcb, "pc01_gr_idle" }, + { 0x86, "pc01_strmout_00" }, + { 0x87, "pc01_strmout_01" }, + { 0xe0, "pc01_trast_00" }, + { 0xe1, "pc01_trast_01" }, + { 0xe2, "pc01_trast_02" }, + { 0xe3, "pc01_trast_03" }, + { 0xe6, "pc01_trast_04" }, + { 0xe7, "pc01_trast_05" }, + { 0x84, "pc01_vattr_00" }, + { 0x85, "pc01_vattr_01" }, + { 0x46, "pc01_vfetch_00", g84_vfetch_sources }, + { 0x47, "pc01_vfetch_01", g84_vfetch_sources }, + { 0x48, "pc01_vfetch_02", g84_vfetch_sources }, + { 0x49, "pc01_vfetch_03", g84_vfetch_sources }, + { 0x4a, "pc01_vfetch_04", g84_vfetch_sources }, + { 0x4b, "pc01_vfetch_05", g84_vfetch_sources }, + { 0x4c, "pc01_vfetch_06", g84_vfetch_sources }, + { 0x4d, "pc01_vfetch_07", g84_vfetch_sources }, + { 0x4e, "pc01_vfetch_08", g84_vfetch_sources }, + { 0x4f, "pc01_vfetch_09", g84_vfetch_sources }, + { 0x50, "pc01_vfetch_0a", g84_vfetch_sources }, + { 0x51, "pc01_vfetch_0b", g84_vfetch_sources }, + { 0x52, "pc01_vfetch_0c", g84_vfetch_sources }, + { 0x53, "pc01_vfetch_0d", g84_vfetch_sources }, + { 0x54, "pc01_vfetch_0e", g84_vfetch_sources }, + { 0x55, "pc01_vfetch_0f", g84_vfetch_sources }, + { 0x56, "pc01_vfetch_10", g84_vfetch_sources }, + { 0x57, "pc01_vfetch_11", g84_vfetch_sources }, + { 0x58, "pc01_vfetch_12", g84_vfetch_sources }, + { 0x59, "pc01_vfetch_13", g84_vfetch_sources }, + { 0x5a, "pc01_vfetch_14", g84_vfetch_sources }, + { 0x5b, "pc01_vfetch_15", g84_vfetch_sources }, + { 0x5c, "pc01_vfetch_16", g84_vfetch_sources }, + { 0x5d, "pc01_vfetch_17", g84_vfetch_sources }, + { 0x5e, "pc01_vfetch_18", g84_vfetch_sources }, + { 0x5f, "pc01_vfetch_19", g84_vfetch_sources }, + { 0x07, "pc01_zcull_00", gt215_zcull_sources }, + { 0x08, "pc01_zcull_01", gt215_zcull_sources }, + { 0x09, "pc01_zcull_02", gt215_zcull_sources }, + { 0x0a, "pc01_zcull_03", gt215_zcull_sources }, + { 0x0b, "pc01_zcull_04", gt215_zcull_sources }, + { 0x0c, "pc01_zcull_05", gt215_zcull_sources }, + { 0xb2, "pc01_unk00" }, + { 0xec, "pc01_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0xe0, (const struct nvkm_specsig[]) { + { 0x64, "pc02_crop_00", gt200_crop_sources }, + { 0x65, "pc02_crop_01", gt200_crop_sources }, + { 0x66, "pc02_crop_02", gt200_crop_sources }, + { 0x67, "pc02_crop_03", gt200_crop_sources }, + { 0x00, "pc02_prop_00", gt200_prop_sources }, + { 0x01, "pc02_prop_01", gt200_prop_sources }, + { 0x02, "pc02_prop_02", gt200_prop_sources }, + { 0x03, "pc02_prop_03", gt200_prop_sources }, + { 0x04, "pc02_prop_04", gt200_prop_sources }, + { 0x05, "pc02_prop_05", gt200_prop_sources }, + { 0x06, "pc02_prop_06", gt200_prop_sources }, + { 0x07, "pc02_prop_07", gt200_prop_sources }, + { 0x80, "pc02_tex_00", gt200_tex_sources }, + { 0x81, "pc02_tex_01", gt200_tex_sources }, + { 0x82, "pc02_tex_02", gt200_tex_sources }, + { 0x83, "pc02_tex_03", gt200_tex_sources }, + { 0x3a, "pc02_tex_04", gt200_tex_sources }, + { 0x3b, "pc02_tex_05", gt200_tex_sources }, + { 0x3c, "pc02_tex_06", gt200_tex_sources }, + { 0x7c, "pc02_zrop_00", nv50_zrop_sources }, + { 0x7d, "pc02_zrop_01", nv50_zrop_sources }, + { 0x7e, "pc02_zrop_02", nv50_zrop_sources }, + { 0x7f, "pc02_zrop_03", nv50_zrop_sources }, + { 0xcc, "pc02_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +int +gt215_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return nv40_pm_new_(gt215_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c new file mode 100644 index 000000000..eba5b3b79 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c @@ -0,0 +1,123 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +static void +nv40_perfctr_init(struct nvkm_pm *pm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct nvkm_device *device = pm->engine.subdev.device; + u32 log = ctr->logic_op; + u32 src = 0x00000000; + int i; + + for (i = 0; i < 4; i++) + src |= ctr->signal[i] << (i * 8); + + nvkm_wr32(device, 0x00a7c0 + dom->addr, 0x00000001 | (dom->mode << 4)); + nvkm_wr32(device, 0x00a400 + dom->addr + (ctr->slot * 0x40), src); + nvkm_wr32(device, 0x00a420 + dom->addr + (ctr->slot * 0x40), log); +} + +static void +nv40_perfctr_read(struct nvkm_pm *pm, struct nvkm_perfdom *dom, + struct nvkm_perfctr *ctr) +{ + struct nvkm_device *device = pm->engine.subdev.device; + + switch (ctr->slot) { + case 0: ctr->ctr = nvkm_rd32(device, 0x00a700 + dom->addr); break; + case 1: ctr->ctr = nvkm_rd32(device, 0x00a6c0 + dom->addr); break; + case 2: ctr->ctr = nvkm_rd32(device, 0x00a680 + dom->addr); break; + case 3: ctr->ctr = nvkm_rd32(device, 0x00a740 + dom->addr); break; + } + dom->clk = nvkm_rd32(device, 0x00a600 + dom->addr); +} + +static void +nv40_perfctr_next(struct nvkm_pm *pm, struct nvkm_perfdom *dom) +{ + struct nvkm_device *device = pm->engine.subdev.device; + struct nv40_pm *nv40pm = container_of(pm, struct nv40_pm, base); + + if (nv40pm->sequence != pm->sequence) { + nvkm_wr32(device, 0x400084, 0x00000020); + nv40pm->sequence = pm->sequence; + } +} + +const struct nvkm_funcdom +nv40_perfctr_func = { + .init = nv40_perfctr_init, + .read = nv40_perfctr_read, + .next = nv40_perfctr_next, +}; + +static const struct nvkm_pm_func +nv40_pm_ = { +}; + +int +nv40_pm_new_(const struct nvkm_specdom *doms, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + struct nv40_pm *pm; + int ret; + + if (!(pm = kzalloc(sizeof(*pm), GFP_KERNEL))) + return -ENOMEM; + *ppm = &pm->base; + + ret = nvkm_pm_ctor(&nv40_pm_, device, type, inst, &pm->base); + if (ret) + return ret; + + return nvkm_perfdom_new(&pm->base, "pc", 0, 0, 0, 4, doms); +} + +static const struct nvkm_specdom +nv40_pm[] = { + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +int +nv40_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return nv40_pm_new_(nv40_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h new file mode 100644 index 000000000..afb798437 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PM_NV40_H__ +#define __NVKM_PM_NV40_H__ +#define nv40_pm(p) container_of((p), struct nv40_pm, base) +#include "priv.h" + +struct nv40_pm { + struct nvkm_pm base; + u32 sequence; +}; + +int nv40_pm_new_(const struct nvkm_specdom *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_pm **); +extern const struct nvkm_funcdom nv40_perfctr_func; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c new file mode 100644 index 000000000..bbd340490 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c @@ -0,0 +1,175 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv40.h" + +const struct nvkm_specsrc +nv50_zcull_sources[] = { + { 0x402ca4, (const struct nvkm_specmux[]) { + { 0x7fff, 0, "unk0" }, + {} + }, "pgraph_zcull_pm_unka4" }, + {} +}; + +const struct nvkm_specsrc +nv50_zrop_sources[] = { + { 0x40708c, (const struct nvkm_specmux[]) { + { 0xf, 0, "sel0", true }, + { 0xf, 16, "sel1", true }, + {} + }, "pgraph_rop0_zrop_pm_mux" }, + {} +}; + +static const struct nvkm_specsrc +nv50_prop_sources[] = { + { 0x40be50, (const struct nvkm_specmux[]) { + { 0x1f, 0, "sel", true }, + {} + }, "pgraph_tpc3_prop_pm_mux" }, + {} +}; + +static const struct nvkm_specsrc +nv50_crop_sources[] = { + { 0x407008, (const struct nvkm_specmux[]) { + { 0x7, 0, "sel0", true }, + { 0x7, 16, "sel1", true }, + {} + }, "pgraph_rop0_crop_pm_mux" }, + {} +}; + +static const struct nvkm_specsrc +nv50_tex_sources[] = { + { 0x40b808, (const struct nvkm_specmux[]) { + { 0x3fff, 0, "unk0" }, + {} + }, "pgraph_tpc3_tex_unk08" }, + {} +}; + +static const struct nvkm_specsrc +nv50_vfetch_sources[] = { + { 0x400c0c, (const struct nvkm_specmux[]) { + { 0x1, 0, "unk0" }, + {} + }, "pgraph_vfetch_unk0c" }, + {} +}; + +static const struct nvkm_specdom +nv50_pm[] = { + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0xf0, (const struct nvkm_specsig[]) { + { 0xc8, "pc01_gr_idle" }, + { 0x7f, "pc01_strmout_00" }, + { 0x80, "pc01_strmout_01" }, + { 0xdc, "pc01_trast_00" }, + { 0xdd, "pc01_trast_01" }, + { 0xde, "pc01_trast_02" }, + { 0xdf, "pc01_trast_03" }, + { 0xe2, "pc01_trast_04" }, + { 0xe3, "pc01_trast_05" }, + { 0x7c, "pc01_vattr_00" }, + { 0x7d, "pc01_vattr_01" }, + { 0x26, "pc01_vfetch_00", nv50_vfetch_sources }, + { 0x27, "pc01_vfetch_01", nv50_vfetch_sources }, + { 0x28, "pc01_vfetch_02", nv50_vfetch_sources }, + { 0x29, "pc01_vfetch_03", nv50_vfetch_sources }, + { 0x2a, "pc01_vfetch_04", nv50_vfetch_sources }, + { 0x2b, "pc01_vfetch_05", nv50_vfetch_sources }, + { 0x2c, "pc01_vfetch_06", nv50_vfetch_sources }, + { 0x2d, "pc01_vfetch_07", nv50_vfetch_sources }, + { 0x2e, "pc01_vfetch_08", nv50_vfetch_sources }, + { 0x2f, "pc01_vfetch_09", nv50_vfetch_sources }, + { 0x30, "pc01_vfetch_0a", nv50_vfetch_sources }, + { 0x31, "pc01_vfetch_0b", nv50_vfetch_sources }, + { 0x32, "pc01_vfetch_0c", nv50_vfetch_sources }, + { 0x33, "pc01_vfetch_0d", nv50_vfetch_sources }, + { 0x34, "pc01_vfetch_0e", nv50_vfetch_sources }, + { 0x35, "pc01_vfetch_0f", nv50_vfetch_sources }, + { 0x36, "pc01_vfetch_10", nv50_vfetch_sources }, + { 0x37, "pc01_vfetch_11", nv50_vfetch_sources }, + { 0x38, "pc01_vfetch_12", nv50_vfetch_sources }, + { 0x39, "pc01_vfetch_13", nv50_vfetch_sources }, + { 0x3a, "pc01_vfetch_14", nv50_vfetch_sources }, + { 0x3b, "pc01_vfetch_15", nv50_vfetch_sources }, + { 0x3c, "pc01_vfetch_16", nv50_vfetch_sources }, + { 0x3d, "pc01_vfetch_17", nv50_vfetch_sources }, + { 0x3e, "pc01_vfetch_18", nv50_vfetch_sources }, + { 0x3f, "pc01_vfetch_19", nv50_vfetch_sources }, + { 0x20, "pc01_zcull_00", nv50_zcull_sources }, + { 0x21, "pc01_zcull_01", nv50_zcull_sources }, + { 0x22, "pc01_zcull_02", nv50_zcull_sources }, + { 0x23, "pc01_zcull_03", nv50_zcull_sources }, + { 0x24, "pc01_zcull_04", nv50_zcull_sources }, + { 0x25, "pc01_zcull_05", nv50_zcull_sources }, + { 0xae, "pc01_unk00" }, + { 0xee, "pc01_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0xf0, (const struct nvkm_specsig[]) { + { 0x52, "pc02_crop_00", nv50_crop_sources }, + { 0x53, "pc02_crop_01", nv50_crop_sources }, + { 0x54, "pc02_crop_02", nv50_crop_sources }, + { 0x55, "pc02_crop_03", nv50_crop_sources }, + { 0x00, "pc02_prop_00", nv50_prop_sources }, + { 0x01, "pc02_prop_01", nv50_prop_sources }, + { 0x02, "pc02_prop_02", nv50_prop_sources }, + { 0x03, "pc02_prop_03", nv50_prop_sources }, + { 0x04, "pc02_prop_04", nv50_prop_sources }, + { 0x05, "pc02_prop_05", nv50_prop_sources }, + { 0x06, "pc02_prop_06", nv50_prop_sources }, + { 0x07, "pc02_prop_07", nv50_prop_sources }, + { 0x70, "pc02_tex_00", nv50_tex_sources }, + { 0x71, "pc02_tex_01", nv50_tex_sources }, + { 0x72, "pc02_tex_02", nv50_tex_sources }, + { 0x73, "pc02_tex_03", nv50_tex_sources }, + { 0x40, "pc02_tex_04", nv50_tex_sources }, + { 0x41, "pc02_tex_05", nv50_tex_sources }, + { 0x42, "pc02_tex_06", nv50_tex_sources }, + { 0x6c, "pc02_zrop_00", nv50_zrop_sources }, + { 0x6d, "pc02_zrop_01", nv50_zrop_sources }, + { 0x6e, "pc02_zrop_02", nv50_zrop_sources }, + { 0x6f, "pc02_zrop_03", nv50_zrop_sources }, + { 0xee, "pc02_trailer" }, + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + { 0x20, (const struct nvkm_specsig[]) { + {} + }, &nv40_perfctr_func }, + {} +}; + +int +nv50_pm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_pm **ppm) +{ + return nv40_pm_new_(nv50_pm, device, type, inst, ppm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h new file mode 100644 index 000000000..6ae25d3e7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PM_PRIV_H__ +#define __NVKM_PM_PRIV_H__ +#define nvkm_pm(p) container_of((p), struct nvkm_pm, engine) +#include + +int nvkm_pm_ctor(const struct nvkm_pm_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_pm *); + +struct nvkm_pm_func { + void (*fini)(struct nvkm_pm *); +}; + +struct nvkm_perfctr { + struct list_head head; + u8 domain; + u8 signal[4]; + u64 source[4][8]; + int slot; + u32 logic_op; + u32 ctr; +}; + +struct nvkm_specmux { + u32 mask; + u8 shift; + const char *name; + bool enable; +}; + +struct nvkm_specsrc { + u32 addr; + const struct nvkm_specmux *mux; + const char *name; +}; + +struct nvkm_perfsrc { + struct list_head head; + char *name; + u32 addr; + u32 mask; + u8 shift; + bool enable; +}; + +extern const struct nvkm_specsrc nv50_zcull_sources[]; +extern const struct nvkm_specsrc nv50_zrop_sources[]; +extern const struct nvkm_specsrc g84_vfetch_sources[]; +extern const struct nvkm_specsrc gt200_crop_sources[]; +extern const struct nvkm_specsrc gt200_prop_sources[]; +extern const struct nvkm_specsrc gt200_tex_sources[]; + +struct nvkm_specsig { + u8 signal; + const char *name; + const struct nvkm_specsrc *source; +}; + +struct nvkm_perfsig { + const char *name; + u8 source[8]; +}; + +struct nvkm_specdom { + u16 signal_nr; + const struct nvkm_specsig *signal; + const struct nvkm_funcdom *func; +}; + +#define nvkm_perfdom(p) container_of((p), struct nvkm_perfdom, object) +#include + +struct nvkm_perfdom { + struct nvkm_object object; + struct nvkm_perfmon *perfmon; + struct list_head head; + struct list_head list; + const struct nvkm_funcdom *func; + struct nvkm_perfctr *ctr[4]; + char name[32]; + u32 addr; + u8 mode; + u32 clk; + u16 signal_nr; + struct nvkm_perfsig signal[]; +}; + +struct nvkm_funcdom { + void (*init)(struct nvkm_pm *, struct nvkm_perfdom *, + struct nvkm_perfctr *); + void (*read)(struct nvkm_pm *, struct nvkm_perfdom *, + struct nvkm_perfctr *); + void (*next)(struct nvkm_pm *, struct nvkm_perfdom *); +}; + +int nvkm_perfdom_new(struct nvkm_pm *, const char *, u32, u32, u32, u32, + const struct nvkm_specdom *); + +#define nvkm_perfmon(p) container_of((p), struct nvkm_perfmon, object) + +struct nvkm_perfmon { + struct nvkm_object object; + struct nvkm_pm *pm; +}; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild new file mode 100644 index 000000000..b6e02ceb1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec/Kbuild @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/sec/g98.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s b/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s new file mode 100644 index 000000000..66b147bd5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s @@ -0,0 +1,698 @@ +/* + * fuc microcode for g98 sec engine + * Copyright (C) 2010 Marcin KoÅ›cielnicki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +.section #g98_sec_data + +ctx_dma: +ctx_dma_query: .b32 0 +ctx_dma_src: .b32 0 +ctx_dma_dst: .b32 0 +.equ #dma_count 3 +ctx_query_address_high: .b32 0 +ctx_query_address_low: .b32 0 +ctx_query_counter: .b32 0 +ctx_cond_address_high: .b32 0 +ctx_cond_address_low: .b32 0 +ctx_cond_off: .b32 0 +ctx_src_address_high: .b32 0 +ctx_src_address_low: .b32 0 +ctx_dst_address_high: .b32 0 +ctx_dst_address_low: .b32 0 +ctx_mode: .b32 0 +.align 16 +ctx_key: .skip 16 +ctx_iv: .skip 16 + +.align 0x80 +swap: +.skip 32 + +.align 8 +common_cmd_dtable: +.b32 #ctx_query_address_high + 0x20000 ~0xff +.b32 #ctx_query_address_low + 0x20000 ~0xfffffff0 +.b32 #ctx_query_counter + 0x20000 ~0xffffffff +.b32 #cmd_query_get + 0x00000 ~1 +.b32 #ctx_cond_address_high + 0x20000 ~0xff +.b32 #ctx_cond_address_low + 0x20000 ~0xfffffff0 +.b32 #cmd_cond_mode + 0x00000 ~7 +.b32 #cmd_wrcache_flush + 0x00000 ~0 +.equ #common_cmd_max 0x88 + + +.align 8 +engine_cmd_dtable: +.b32 #ctx_key + 0x0 + 0x20000 ~0xffffffff +.b32 #ctx_key + 0x4 + 0x20000 ~0xffffffff +.b32 #ctx_key + 0x8 + 0x20000 ~0xffffffff +.b32 #ctx_key + 0xc + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0x0 + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0x4 + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0x8 + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0xc + 0x20000 ~0xffffffff +.b32 #ctx_src_address_high + 0x20000 ~0xff +.b32 #ctx_src_address_low + 0x20000 ~0xfffffff0 +.b32 #ctx_dst_address_high + 0x20000 ~0xff +.b32 #ctx_dst_address_low + 0x20000 ~0xfffffff0 +.b32 #sec_cmd_mode + 0x00000 ~0xf +.b32 #sec_cmd_length + 0x10000 ~0x0ffffff0 +.equ #engine_cmd_max 0xce + +.align 4 +sec_dtable: +.b16 #sec_copy_prep #sec_do_inout +.b16 #sec_store_prep #sec_do_out +.b16 #sec_ecb_e_prep #sec_do_inout +.b16 #sec_ecb_d_prep #sec_do_inout +.b16 #sec_cbc_e_prep #sec_do_inout +.b16 #sec_cbc_d_prep #sec_do_inout +.b16 #sec_pcbc_e_prep #sec_do_inout +.b16 #sec_pcbc_d_prep #sec_do_inout +.b16 #sec_cfb_e_prep #sec_do_inout +.b16 #sec_cfb_d_prep #sec_do_inout +.b16 #sec_ofb_prep #sec_do_inout +.b16 #sec_ctr_prep #sec_do_inout +.b16 #sec_cbc_mac_prep #sec_do_in +.b16 #sec_cmac_finish_complete_prep #sec_do_in +.b16 #sec_cmac_finish_partial_prep #sec_do_in + +.align 0x100 + +.section #g98_sec_code + + // $r0 is always set to 0 in our code - this allows some space savings. + clear b32 $r0 + + // set up the interrupt handler + mov $r1 #ih + mov $iv0 $r1 + + // init stack pointer + mov $sp $r0 + + // set interrupt dispatch - route timer, fifo, ctxswitch to i0, others to host + movw $r1 0xfff0 + sethi $r1 0 + mov $r2 0x400 + iowr I[$r2 + 0x300] $r1 + + // enable the interrupts + or $r1 0xc + iowr I[$r2] $r1 + + // enable fifo access and context switching + mov $r1 3 + mov $r2 0x1200 + iowr I[$r2] $r1 + + // enable i0 delivery + bset $flags ie0 + + // sleep forver, waking only for interrupts. + bset $flags $p0 + spin: + sleep $p0 + bra #spin + +// i0 handler +ih: + // see which interrupts we got + iord $r1 I[$r0 + 0x200] + + and $r2 $r1 0x8 + cmpu b32 $r2 0 + bra e #noctx + + // context switch... prepare the regs for xfer + mov $r2 0x7700 + mov $xtargets $r2 + mov $xdbase $r0 + // 128-byte context. + mov $r2 0 + sethi $r2 0x50000 + + // read current channel + mov $r3 0x1400 + iord $r4 I[$r3] + // if bit 30 set, it's active, so we have to unload it first. + shl b32 $r5 $r4 1 + cmps b32 $r5 0 + bra nc #ctxload + + // unload the current channel - save the context + xdst $r0 $r2 + xdwait + // and clear bit 30, then write back + bclr $r4 0x1e + iowr I[$r3] $r4 + // tell PFIFO we unloaded + mov $r4 1 + iowr I[$r3 + 0x200] $r4 + + bra #noctx + + ctxload: + // no channel loaded - perhaps we're requested to load one + iord $r4 I[$r3 + 0x100] + shl b32 $r15 $r4 1 + cmps b32 $r15 0 + // if bit 30 of next channel not set, probably PFIFO is just + // killing a context. do a faux load, without the active bit. + bra nc #dummyload + + // ok, do a real context load. + xdld $r0 $r2 + xdwait + mov $r5 #ctx_dma + mov $r6 #dma_count - 1 + ctxload_dma_loop: + ld b32 $r7 D[$r5 + $r6 * 4] + add b32 $r8 $r6 0x180 + shl b32 $r8 8 + iowr I[$r8] $r7 + sub b32 $r6 1 + bra nc #ctxload_dma_loop + + dummyload: + // tell PFIFO we're done + mov $r5 2 + iowr I[$r3 + 0x200] $r5 + + noctx: + and $r2 $r1 0x4 + cmpu b32 $r2 0 + bra e #nocmd + + // incoming fifo command. + mov $r3 0x1900 + iord $r2 I[$r3 + 0x100] + iord $r3 I[$r3] + // extract the method + and $r4 $r2 0x7ff + // shift the addr to proper position if we need to interrupt later + shl b32 $r2 0x10 + + // mthd 0 and 0x100 [NAME, NOP]: ignore + and $r5 $r4 0x7bf + cmpu b32 $r5 0 + bra e #cmddone + + mov $r5 #engine_cmd_dtable - 0xc0 * 8 + mov $r6 #engine_cmd_max + cmpu b32 $r4 0xc0 + bra nc #dtable_cmd + mov $r5 #common_cmd_dtable - 0x80 * 8 + mov $r6 #common_cmd_max + cmpu b32 $r4 0x80 + bra nc #dtable_cmd + cmpu b32 $r4 0x60 + bra nc #dma_cmd + cmpu b32 $r4 0x50 + bra ne #illegal_mthd + + // mthd 0x140: PM_TRIGGER + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x20000 + iowr I[$r2] $r3 + bra #cmddone + + dma_cmd: + // mthd 0x180...: DMA_* + cmpu b32 $r4 0x60+#dma_count + bra nc #illegal_mthd + shl b32 $r5 $r4 2 + add b32 $r5 ((#ctx_dma - 0x60 * 4) & 0xffff) + bset $r3 0x1e + st b32 D[$r5] $r3 + add b32 $r4 0x180 - 0x60 + shl b32 $r4 8 + iowr I[$r4] $r3 + bra #cmddone + + dtable_cmd: + cmpu b32 $r4 $r6 + bra nc #illegal_mthd + shl b32 $r4 3 + add b32 $r4 $r5 + ld b32 $r5 D[$r4 + 4] + and $r5 $r3 + cmpu b32 $r5 0 + bra ne #invalid_bitfield + ld b16 $r5 D[$r4] + ld b16 $r6 D[$r4 + 2] + cmpu b32 $r6 2 + bra e #cmd_setctx + ld b32 $r7 D[$r0 + #ctx_cond_off] + and $r6 $r7 + cmpu b32 $r6 1 + bra e #cmddone + call $r5 + bra $p1 #dispatch_error + bra #cmddone + + cmd_setctx: + st b32 D[$r5] $r3 + bra #cmddone + + + invalid_bitfield: + or $r2 1 + dispatch_error: + illegal_mthd: + mov $r4 0x1000 + iowr I[$r4] $r2 + iowr I[$r4 + 0x100] $r3 + mov $r4 0x40 + iowr I[$r0] $r4 + + im_loop: + iord $r4 I[$r0 + 0x200] + and $r4 0x40 + cmpu b32 $r4 0 + bra ne #im_loop + + cmddone: + // remove the command from FIFO + mov $r3 0x1d00 + mov $r4 1 + iowr I[$r3] $r4 + + nocmd: + // ack the processed interrupts + and $r1 $r1 0xc + iowr I[$r0 + 0x100] $r1 +iret + +cmd_query_get: + // if bit 0 of param set, trigger interrupt afterwards. + setp $p1 $r3 + or $r2 3 + + // read PTIMER, beware of races... + mov $r4 0xb00 + ptimer_retry: + iord $r6 I[$r4 + 0x100] + iord $r5 I[$r4] + iord $r7 I[$r4 + 0x100] + cmpu b32 $r6 $r7 + bra ne #ptimer_retry + + // prepare the query structure + ld b32 $r4 D[$r0 + #ctx_query_counter] + st b32 D[$r0 + #swap + 0x0] $r4 + st b32 D[$r0 + #swap + 0x4] $r0 + st b32 D[$r0 + #swap + 0x8] $r5 + st b32 D[$r0 + #swap + 0xc] $r6 + + // will use target 0, DMA_QUERY. + mov $xtargets $r0 + + ld b32 $r4 D[$r0 + #ctx_query_address_high] + shl b32 $r4 0x18 + mov $xdbase $r4 + + ld b32 $r4 D[$r0 + #ctx_query_address_low] + mov $r5 #swap + sethi $r5 0x20000 + xdst $r4 $r5 + xdwait + + ret + +cmd_cond_mode: + // if >= 5, INVALID_ENUM + bset $flags $p1 + or $r2 2 + cmpu b32 $r3 5 + bra nc #return + + // otherwise, no error. + bclr $flags $p1 + + // if < 2, no QUERY object is involved + cmpu b32 $r3 2 + bra nc #cmd_cond_mode_queryful + + xor $r3 1 + st b32 D[$r0 + #ctx_cond_off] $r3 + return: + ret + + cmd_cond_mode_queryful: + // ok, will need to pull a QUERY object, prepare offsets + ld b32 $r4 D[$r0 + #ctx_cond_address_high] + ld b32 $r5 D[$r0 + #ctx_cond_address_low] + and $r6 $r5 0xff + shr b32 $r5 8 + shl b32 $r4 0x18 + or $r4 $r5 + mov $xdbase $r4 + mov $xtargets $r0 + + // pull the first one + mov $r5 #swap + sethi $r5 0x20000 + xdld $r6 $r5 + + // if == 2, only a single QUERY is involved... + cmpu b32 $r3 2 + bra ne #cmd_cond_mode_double + + xdwait + ld b32 $r4 D[$r0 + #swap + 4] + cmpu b32 $r4 0 + xbit $r4 $flags z + st b32 D[$r0 + #ctx_cond_off] $r4 + ret + + // ok, we'll need to pull second one too + cmd_cond_mode_double: + add b32 $r6 0x10 + add b32 $r5 0x10 + xdld $r6 $r5 + xdwait + + // compare COUNTERs + ld b32 $r5 D[$r0 + #swap + 0x00] + ld b32 $r6 D[$r0 + #swap + 0x10] + cmpu b32 $r5 $r6 + xbit $r4 $flags z + + // compare RESen + ld b32 $r5 D[$r0 + #swap + 0x04] + ld b32 $r6 D[$r0 + #swap + 0x14] + cmpu b32 $r5 $r6 + xbit $r5 $flags z + and $r4 $r5 + + // and negate or not, depending on mode + cmpu b32 $r3 3 + xbit $r5 $flags z + xor $r4 $r5 + st b32 D[$r0 + #ctx_cond_off] $r4 + ret + +cmd_wrcache_flush: + bclr $flags $p1 + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x10000 + iowr I[$r2] $r3 + ret + +sec_cmd_mode: + // if >= 0xf, INVALID_ENUM + bset $flags $p1 + or $r2 2 + cmpu b32 $r3 0xf + bra nc #sec_cmd_mode_return + + bclr $flags $p1 + st b32 D[$r0 + #ctx_mode] $r3 + + sec_cmd_mode_return: + ret + +sec_cmd_length: + // nop if length == 0 + cmpu b32 $r3 0 + bra e #sec_cmd_mode_return + + // init key, IV + cxset 3 + mov $r4 #ctx_key + sethi $r4 0x70000 + xdst $r0 $r4 + mov $r4 #ctx_iv + sethi $r4 0x60000 + xdst $r0 $r4 + xdwait + ckeyreg $c7 + + // prepare the targets + mov $r4 0x2100 + mov $xtargets $r4 + + // prepare src address + ld b32 $r4 D[$r0 + #ctx_src_address_high] + ld b32 $r5 D[$r0 + #ctx_src_address_low] + shr b32 $r8 $r5 8 + shl b32 $r4 0x18 + or $r4 $r8 + and $r5 $r5 0xff + + // prepare dst address + ld b32 $r6 D[$r0 + #ctx_dst_address_high] + ld b32 $r7 D[$r0 + #ctx_dst_address_low] + shr b32 $r8 $r7 8 + shl b32 $r6 0x18 + or $r6 $r8 + and $r7 $r7 0xff + + // find the proper prep & do functions + ld b32 $r8 D[$r0 + #ctx_mode] + shl b32 $r8 2 + + // run prep + ld b16 $r9 D[$r8 + #sec_dtable] + call $r9 + + // do it + ld b16 $r9 D[$r8 + #sec_dtable + 2] + call $r9 + cxset 1 + xdwait + cxset 0x61 + xdwait + xdwait + + // update src address + shr b32 $r8 $r4 0x18 + shl b32 $r9 $r4 8 + add b32 $r9 $r5 + adc b32 $r8 0 + st b32 D[$r0 + #ctx_src_address_high] $r8 + st b32 D[$r0 + #ctx_src_address_low] $r9 + + // update dst address + shr b32 $r8 $r6 0x18 + shl b32 $r9 $r6 8 + add b32 $r9 $r7 + adc b32 $r8 0 + st b32 D[$r0 + #ctx_dst_address_high] $r8 + st b32 D[$r0 + #ctx_dst_address_low] $r9 + + // pull updated IV + cxset 2 + mov $r4 #ctx_iv + sethi $r4 0x60000 + xdld $r0 $r4 + xdwait + + ret + + +sec_copy_prep: + cs0begin 2 + cxsin $c0 + cxsout $c0 + ret + +sec_store_prep: + cs0begin 1 + cxsout $c6 + ret + +sec_ecb_e_prep: + cs0begin 3 + cxsin $c0 + cenc $c0 $c0 + cxsout $c0 + ret + +sec_ecb_d_prep: + ckexp $c7 $c7 + cs0begin 3 + cxsin $c0 + cdec $c0 $c0 + cxsout $c0 + ret + +sec_cbc_e_prep: + cs0begin 4 + cxsin $c0 + cxor $c6 $c0 + cenc $c6 $c6 + cxsout $c6 + ret + +sec_cbc_d_prep: + ckexp $c7 $c7 + cs0begin 5 + cmov $c2 $c6 + cxsin $c6 + cdec $c0 $c6 + cxor $c0 $c2 + cxsout $c0 + ret + +sec_pcbc_e_prep: + cs0begin 5 + cxsin $c0 + cxor $c6 $c0 + cenc $c6 $c6 + cxsout $c6 + cxor $c6 $c0 + ret + +sec_pcbc_d_prep: + ckexp $c7 $c7 + cs0begin 5 + cxsin $c0 + cdec $c1 $c0 + cxor $c6 $c1 + cxsout $c6 + cxor $c6 $c0 + ret + +sec_cfb_e_prep: + cs0begin 4 + cenc $c6 $c6 + cxsin $c0 + cxor $c6 $c0 + cxsout $c6 + ret + +sec_cfb_d_prep: + cs0begin 4 + cenc $c0 $c6 + cxsin $c6 + cxor $c0 $c6 + cxsout $c0 + ret + +sec_ofb_prep: + cs0begin 4 + cenc $c6 $c6 + cxsin $c0 + cxor $c0 $c6 + cxsout $c0 + ret + +sec_ctr_prep: + cs0begin 5 + cenc $c1 $c6 + cadd $c6 1 + cxsin $c0 + cxor $c0 $c1 + cxsout $c0 + ret + +sec_cbc_mac_prep: + cs0begin 3 + cxsin $c0 + cxor $c6 $c0 + cenc $c6 $c6 + ret + +sec_cmac_finish_complete_prep: + cs0begin 7 + cxsin $c0 + cxor $c6 $c0 + cxor $c0 $c0 + cenc $c0 $c0 + cprecmac $c0 $c0 + cxor $c6 $c0 + cenc $c6 $c6 + ret + +sec_cmac_finish_partial_prep: + cs0begin 8 + cxsin $c0 + cxor $c6 $c0 + cxor $c0 $c0 + cenc $c0 $c0 + cprecmac $c0 $c0 + cprecmac $c0 $c0 + cxor $c6 $c0 + cenc $c6 $c6 + ret + +// TODO +sec_do_in: + add b32 $r3 $r5 + mov $xdbase $r4 + mov $r9 #swap + sethi $r9 0x20000 + sec_do_in_loop: + xdld $r5 $r9 + xdwait + cxset 0x22 + xdst $r0 $r9 + cs0exec 1 + xdwait + add b32 $r5 0x10 + cmpu b32 $r5 $r3 + bra ne #sec_do_in_loop + cxset 1 + xdwait + ret + +sec_do_out: + add b32 $r3 $r7 + mov $xdbase $r6 + mov $r9 #swap + sethi $r9 0x20000 + sec_do_out_loop: + cs0exec 1 + cxset 0x61 + xdld $r7 $r9 + xdst $r7 $r9 + cxset 1 + xdwait + add b32 $r7 0x10 + cmpu b32 $r7 $r3 + bra ne #sec_do_out_loop + ret + +sec_do_inout: + add b32 $r3 $r5 + mov $r9 #swap + sethi $r9 0x20000 + sec_do_inout_loop: + mov $xdbase $r4 + xdld $r5 $r9 + xdwait + cxset 0x21 + xdst $r0 $r9 + cs0exec 1 + cxset 0x61 + mov $xdbase $r6 + xdld $r7 $r9 + xdst $r7 $r9 + cxset 1 + xdwait + add b32 $r5 0x10 + add b32 $r7 0x10 + cmpu b32 $r5 $r3 + bra ne #sec_do_inout_loop + ret + +.align 0x100 diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h b/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h new file mode 100644 index 000000000..fe90f2e05 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec/fuc/g98.fuc0s.h @@ -0,0 +1,585 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t g98_sec_data[] = { +/* 0x0000: ctx_dma */ +/* 0x0000: ctx_dma_query */ + 0x00000000, +/* 0x0004: ctx_dma_src */ + 0x00000000, +/* 0x0008: ctx_dma_dst */ + 0x00000000, +/* 0x000c: ctx_query_address_high */ + 0x00000000, +/* 0x0010: ctx_query_address_low */ + 0x00000000, +/* 0x0014: ctx_query_counter */ + 0x00000000, +/* 0x0018: ctx_cond_address_high */ + 0x00000000, +/* 0x001c: ctx_cond_address_low */ + 0x00000000, +/* 0x0020: ctx_cond_off */ + 0x00000000, +/* 0x0024: ctx_src_address_high */ + 0x00000000, +/* 0x0028: ctx_src_address_low */ + 0x00000000, +/* 0x002c: ctx_dst_address_high */ + 0x00000000, +/* 0x0030: ctx_dst_address_low */ + 0x00000000, +/* 0x0034: ctx_mode */ + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0040: ctx_key */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0050: ctx_iv */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0080: swap */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x00a0: common_cmd_dtable */ + 0x0002000c, + 0xffffff00, + 0x00020010, + 0x0000000f, + 0x00020014, + 0x00000000, + 0x00000192, + 0xfffffffe, + 0x00020018, + 0xffffff00, + 0x0002001c, + 0x0000000f, + 0x000001d7, + 0xfffffff8, + 0x00000260, + 0xffffffff, +/* 0x00e0: engine_cmd_dtable */ + 0x00020040, + 0x00000000, + 0x00020044, + 0x00000000, + 0x00020048, + 0x00000000, + 0x0002004c, + 0x00000000, + 0x00020050, + 0x00000000, + 0x00020054, + 0x00000000, + 0x00020058, + 0x00000000, + 0x0002005c, + 0x00000000, + 0x00020024, + 0xffffff00, + 0x00020028, + 0x0000000f, + 0x0002002c, + 0xffffff00, + 0x00020030, + 0x0000000f, + 0x00000271, + 0xfffffff0, + 0x00010285, + 0xf000000f, +/* 0x0150: sec_dtable */ + 0x04db0321, + 0x04b1032f, + 0x04db0339, + 0x04db034b, + 0x04db0361, + 0x04db0377, + 0x04db0395, + 0x04db03af, + 0x04db03cd, + 0x04db03e3, + 0x04db03f9, + 0x04db040f, + 0x04830429, + 0x0483043b, + 0x0483045d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t g98_sec_code[] = { + 0x17f004bd, + 0x0010fe35, + 0xf10004fe, + 0xf0fff017, + 0x27f10013, + 0x21d00400, + 0x0c15f0c0, + 0xf00021d0, + 0x27f10317, + 0x21d01200, + 0x1031f400, +/* 0x002f: spin */ + 0xf40031f4, + 0x0ef40028, +/* 0x0035: ih */ + 0x8001cffd, + 0xb00812c4, + 0x0bf40024, + 0x0027f167, + 0x002bfe77, + 0xf00007fe, + 0x23f00027, + 0x0037f105, + 0x0034cf14, + 0xb0014594, + 0x18f40055, + 0x0602fa17, + 0x4af003f8, + 0x0034d01e, + 0xd00147f0, + 0x0ef48034, +/* 0x0075: ctxload */ + 0x4034cf33, + 0xb0014f94, + 0x18f400f5, + 0x0502fa21, + 0x57f003f8, + 0x0267f000, +/* 0x008c: ctxload_dma_loop */ + 0xa07856bc, + 0xb6018068, + 0x87d00884, + 0x0162b600, +/* 0x009f: dummyload */ + 0xf0f018f4, + 0x35d00257, +/* 0x00a5: noctx */ + 0x0412c480, + 0xf50024b0, + 0xf100df0b, + 0xcf190037, + 0x33cf4032, + 0xff24e400, + 0x1024b607, + 0x07bf45e4, + 0xf50054b0, + 0xf100b90b, + 0xf1fae057, + 0xb000ce67, + 0x18f4c044, + 0xa057f14d, + 0x8867f1fc, + 0x8044b000, + 0xb03f18f4, + 0x18f46044, + 0x5044b019, + 0xf1741bf4, + 0xbd220027, + 0x0233f034, + 0xf50023d0, +/* 0x0103: dma_cmd */ + 0xb000810e, + 0x18f46344, + 0x0245945e, + 0xfe8050b7, + 0x801e39f0, + 0x40b70053, + 0x44b60120, + 0x0043d008, +/* 0x0123: dtable_cmd */ + 0xb8600ef4, + 0x18f40446, + 0x0344b63e, + 0x980045bb, + 0x53fd0145, + 0x0054b004, + 0x58291bf4, + 0x46580045, + 0x0264b001, + 0x98170bf4, + 0x67fd0807, + 0x0164b004, + 0xf9300bf4, + 0x0f01f455, +/* 0x015b: cmd_setctx */ + 0x80280ef4, + 0x0ef40053, +/* 0x0161: invalid_bitfield */ + 0x0125f022, +/* 0x0164: dispatch_error */ +/* 0x0164: illegal_mthd */ + 0x100047f1, + 0xd00042d0, + 0x47f04043, + 0x0004d040, +/* 0x0174: im_loop */ + 0xf08004cf, + 0x44b04044, + 0xf71bf400, +/* 0x0180: cmddone */ + 0x1d0037f1, + 0xd00147f0, +/* 0x018a: nocmd */ + 0x11c40034, + 0x4001d00c, +/* 0x0192: cmd_query_get */ + 0x38f201f8, + 0x0325f001, + 0x0b0047f1, +/* 0x019c: ptimer_retry */ + 0xcf4046cf, + 0x47cf0045, + 0x0467b840, + 0x98f41bf4, + 0x04800504, + 0x21008020, + 0x80220580, + 0x0bfe2306, + 0x03049800, + 0xfe1844b6, + 0x04980047, + 0x8057f104, + 0x0253f000, + 0xf80645fa, +/* 0x01d7: cmd_cond_mode */ + 0xf400f803, + 0x25f00131, + 0x0534b002, + 0xf41218f4, + 0x34b00132, + 0x0b18f402, + 0x800136f0, +/* 0x01f2: return */ + 0x00f80803, +/* 0x01f4: cmd_cond_mode_queryful */ + 0x98060498, + 0x56c40705, + 0x0855b6ff, + 0xfd1844b6, + 0x47fe0545, + 0x000bfe00, + 0x008057f1, + 0xfa0253f0, + 0x34b00565, + 0x131bf402, + 0x049803f8, + 0x0044b021, + 0x800b4cf0, + 0x00f80804, +/* 0x022c: cmd_cond_mode_double */ + 0xb61060b6, + 0x65fa1050, + 0x9803f805, + 0x06982005, + 0x0456b824, + 0x980b4cf0, + 0x06982105, + 0x0456b825, + 0xfd0b5cf0, + 0x34b00445, + 0x0b5cf003, + 0x800645fd, + 0x00f80804, +/* 0x0260: cmd_wrcache_flush */ + 0xf10132f4, + 0xbd220027, + 0x0133f034, + 0xf80023d0, +/* 0x0271: sec_cmd_mode */ + 0x0131f400, + 0xb00225f0, + 0x18f40f34, + 0x0132f409, +/* 0x0283: sec_cmd_mode_return */ + 0xf80d0380, +/* 0x0285: sec_cmd_length */ + 0x0034b000, + 0xf4fb0bf4, + 0x47f0033c, + 0x0743f040, + 0xf00604fa, + 0x43f05047, + 0x0604fa06, + 0x3cf503f8, + 0x47f1c407, + 0x4bfe2100, + 0x09049800, + 0x950a0598, + 0x44b60858, + 0x0548fd18, + 0x98ff55c4, + 0x07980b06, + 0x0878950c, + 0xfd1864b6, + 0x77c40568, + 0x0d0898ff, + 0x580284b6, + 0x95f9a889, + 0xf9a98958, + 0x013cf495, + 0x3cf403f8, + 0xf803f861, + 0x18489503, + 0xbb084994, + 0x81b60095, + 0x09088000, + 0x950a0980, + 0x69941868, + 0x0097bb08, + 0x800081b6, + 0x09800b08, + 0x023cf40c, + 0xf05047f0, + 0x04fa0643, + 0xf803f805, +/* 0x0321: sec_copy_prep */ + 0x203cf500, + 0x003cf594, + 0x003cf588, +/* 0x032f: sec_store_prep */ + 0xf500f88c, + 0xf594103c, + 0xf88c063c, +/* 0x0339: sec_ecb_e_prep */ + 0x303cf500, + 0x003cf594, + 0x003cf588, + 0x003cf5d0, +/* 0x034b: sec_ecb_d_prep */ + 0xf500f88c, + 0xf5c8773c, + 0xf594303c, + 0xf588003c, + 0xf5d4003c, + 0xf88c003c, +/* 0x0361: sec_cbc_e_prep */ + 0x403cf500, + 0x003cf594, + 0x063cf588, + 0x663cf5ac, + 0x063cf5d0, +/* 0x0377: sec_cbc_d_prep */ + 0xf500f88c, + 0xf5c8773c, + 0xf594503c, + 0xf584623c, + 0xf588063c, + 0xf5d4603c, + 0xf5ac203c, + 0xf88c003c, +/* 0x0395: sec_pcbc_e_prep */ + 0x503cf500, + 0x003cf594, + 0x063cf588, + 0x663cf5ac, + 0x063cf5d0, + 0x063cf58c, +/* 0x03af: sec_pcbc_d_prep */ + 0xf500f8ac, + 0xf5c8773c, + 0xf594503c, + 0xf588003c, + 0xf5d4013c, + 0xf5ac163c, + 0xf58c063c, + 0xf8ac063c, +/* 0x03cd: sec_cfb_e_prep */ + 0x403cf500, + 0x663cf594, + 0x003cf5d0, + 0x063cf588, + 0x063cf5ac, +/* 0x03e3: sec_cfb_d_prep */ + 0xf500f88c, + 0xf594403c, + 0xf5d0603c, + 0xf588063c, + 0xf5ac603c, + 0xf88c003c, +/* 0x03f9: sec_ofb_prep */ + 0x403cf500, + 0x663cf594, + 0x003cf5d0, + 0x603cf588, + 0x003cf5ac, +/* 0x040f: sec_ctr_prep */ + 0xf500f88c, + 0xf594503c, + 0xf5d0613c, + 0xf5b0163c, + 0xf588003c, + 0xf5ac103c, + 0xf88c003c, +/* 0x0429: sec_cbc_mac_prep */ + 0x303cf500, + 0x003cf594, + 0x063cf588, + 0x663cf5ac, +/* 0x043b: sec_cmac_finish_complete_prep */ + 0xf500f8d0, + 0xf594703c, + 0xf588003c, + 0xf5ac063c, + 0xf5ac003c, + 0xf5d0003c, + 0xf5bc003c, + 0xf5ac063c, + 0xf8d0663c, +/* 0x045d: sec_cmac_finish_partial_prep */ + 0x803cf500, + 0x003cf594, + 0x063cf588, + 0x003cf5ac, + 0x003cf5ac, + 0x003cf5d0, + 0x003cf5bc, + 0x063cf5bc, + 0x663cf5ac, +/* 0x0483: sec_do_in */ + 0xbb00f8d0, + 0x47fe0035, + 0x8097f100, + 0x0293f000, +/* 0x0490: sec_do_in_loop */ + 0xf80559fa, + 0x223cf403, + 0xf50609fa, + 0xf898103c, + 0x1050b603, + 0xf40453b8, + 0x3cf4e91b, + 0xf803f801, +/* 0x04b1: sec_do_out */ + 0x0037bb00, + 0xf10067fe, + 0xf0008097, +/* 0x04be: sec_do_out_loop */ + 0x3cf50293, + 0x3cf49810, + 0x0579fa61, + 0xf40679fa, + 0x03f8013c, + 0xb81070b6, + 0x1bf40473, +/* 0x04db: sec_do_inout */ + 0xbb00f8e8, + 0x97f10035, + 0x93f00080, +/* 0x04e5: sec_do_inout_loop */ + 0x0047fe02, + 0xf80559fa, + 0x213cf403, + 0xf50609fa, + 0xf498103c, + 0x67fe613c, + 0x0579fa00, + 0xf40679fa, + 0x03f8013c, + 0xb61050b6, + 0x53b81070, + 0xd41bf404, + 0x000000f8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c b/drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c new file mode 100644 index 000000000..1b87df03c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c @@ -0,0 +1,81 @@ +/* + * 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 +#include +#include "fuc/g98.fuc0s.h" + +#include +#include +#include + +#include + +static const struct nvkm_enum g98_sec_isr_error_name[] = { + { 0x0000, "ILLEGAL_MTHD" }, + { 0x0001, "INVALID_BITFIELD" }, + { 0x0002, "INVALID_ENUM" }, + { 0x0003, "QUERY" }, + {} +}; + +static void +g98_sec_intr(struct nvkm_falcon *sec, struct nvkm_fifo_chan *chan) +{ + struct nvkm_subdev *subdev = &sec->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 ssta = nvkm_rd32(device, 0x087040) & 0x0000ffff; + u32 addr = nvkm_rd32(device, 0x087040) >> 16; + u32 mthd = (addr & 0x07ff) << 2; + u32 subc = (addr & 0x3800) >> 11; + u32 data = nvkm_rd32(device, 0x087044); + const struct nvkm_enum *en = + nvkm_enum_find(g98_sec_isr_error_name, ssta); + + nvkm_error(subdev, "DISPATCH_ERROR %04x [%s] ch %d [%010llx %s] " + "subc %d mthd %04x data %08x\n", ssta, + en ? en->name : "UNKNOWN", chan ? chan->chid : -1, + chan ? chan->inst->addr : 0, + chan ? chan->object.client->name : "unknown", + subc, mthd, data); +} + +static const struct nvkm_falcon_func +g98_sec = { + .code.data = g98_sec_code, + .code.size = sizeof(g98_sec_code), + .data.data = g98_sec_data, + .data.size = sizeof(g98_sec_data), + .intr = g98_sec_intr, + .sclass = { + { -1, -1, G98_SEC }, + {} + } +}; + +int +g98_sec_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_falcon_new_(&g98_sec, device, type, inst, true, 0x087000, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild new file mode 100644 index 000000000..63cd2be3d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/sec2/base.o +nvkm-y += nvkm/engine/sec2/gp102.o +nvkm-y += nvkm/engine/sec2/gp108.o +nvkm-y += nvkm/engine/sec2/tu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c new file mode 100644 index 000000000..092c6d0b8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include + +static void +nvkm_sec2_recv(struct work_struct *work) +{ + struct nvkm_sec2 *sec2 = container_of(work, typeof(*sec2), work); + + if (!sec2->initmsg_received) { + int ret = sec2->func->initmsg(sec2); + if (ret) { + nvkm_error(&sec2->engine.subdev, + "error parsing init message: %d\n", ret); + return; + } + + sec2->initmsg_received = true; + } + + nvkm_falcon_msgq_recv(sec2->msgq); +} + +static void +nvkm_sec2_intr(struct nvkm_engine *engine) +{ + struct nvkm_sec2 *sec2 = nvkm_sec2(engine); + sec2->func->intr(sec2); +} + +static int +nvkm_sec2_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_sec2 *sec2 = nvkm_sec2(engine); + + flush_work(&sec2->work); + + if (suspend) { + nvkm_falcon_cmdq_fini(sec2->cmdq); + sec2->initmsg_received = false; + } + + return 0; +} + +static void * +nvkm_sec2_dtor(struct nvkm_engine *engine) +{ + struct nvkm_sec2 *sec2 = nvkm_sec2(engine); + nvkm_falcon_msgq_del(&sec2->msgq); + nvkm_falcon_cmdq_del(&sec2->cmdq); + nvkm_falcon_qmgr_del(&sec2->qmgr); + nvkm_falcon_dtor(&sec2->falcon); + return sec2; +} + +static const struct nvkm_engine_func +nvkm_sec2 = { + .dtor = nvkm_sec2_dtor, + .fini = nvkm_sec2_fini, + .intr = nvkm_sec2_intr, +}; + +int +nvkm_sec2_new_(const struct nvkm_sec2_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, u32 addr, struct nvkm_sec2 **psec2) +{ + struct nvkm_sec2 *sec2; + int ret; + + if (!(sec2 = *psec2 = kzalloc(sizeof(*sec2), GFP_KERNEL))) + return -ENOMEM; + + ret = nvkm_engine_ctor(&nvkm_sec2, device, type, inst, true, &sec2->engine); + if (ret) + return ret; + + fwif = nvkm_firmware_load(&sec2->engine.subdev, fwif, "Sec2", sec2); + if (IS_ERR(fwif)) + return PTR_ERR(fwif); + + sec2->func = fwif->func; + + ret = nvkm_falcon_ctor(sec2->func->flcn, &sec2->engine.subdev, + sec2->engine.subdev.name, addr, &sec2->falcon); + if (ret) + return ret; + + if ((ret = nvkm_falcon_qmgr_new(&sec2->falcon, &sec2->qmgr)) || + (ret = nvkm_falcon_cmdq_new(sec2->qmgr, "cmdq", &sec2->cmdq)) || + (ret = nvkm_falcon_msgq_new(sec2->qmgr, "msgq", &sec2->msgq))) + return ret; + + INIT_WORK(&sec2->work, nvkm_sec2_recv); + return 0; +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c new file mode 100644 index 000000000..44e39f574 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include + +#include +#include + +int +gp102_sec2_nofw(struct nvkm_sec2 *sec2, int ver, + const struct nvkm_sec2_fwif *fwif) +{ + nvkm_warn(&sec2->engine.subdev, "firmware unavailable\n"); + return 0; +} + +static int +gp102_sec2_acr_bootstrap_falcon_callback(void *priv, struct nvfw_falcon_msg *hdr) +{ + struct nv_sec2_acr_bootstrap_falcon_msg *msg = + container_of(hdr, typeof(*msg), msg.hdr); + struct nvkm_subdev *subdev = priv; + const char *name = nvkm_acr_lsf_id(msg->falcon_id); + + if (msg->error_code) { + nvkm_error(subdev, "ACR_BOOTSTRAP_FALCON failed for " + "falcon %d [%s]: %08x\n", + msg->falcon_id, name, msg->error_code); + return -EINVAL; + } + + nvkm_debug(subdev, "%s booted\n", name); + return 0; +} + +static int +gp102_sec2_acr_bootstrap_falcon(struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id) +{ + struct nvkm_sec2 *sec2 = container_of(falcon, typeof(*sec2), falcon); + struct nv_sec2_acr_bootstrap_falcon_cmd cmd = { + .cmd.hdr.unit_id = sec2->func->unit_acr, + .cmd.hdr.size = sizeof(cmd), + .cmd.cmd_type = NV_SEC2_ACR_CMD_BOOTSTRAP_FALCON, + .flags = NV_SEC2_ACR_BOOTSTRAP_FALCON_FLAGS_RESET_YES, + .falcon_id = id, + }; + + return nvkm_falcon_cmdq_send(sec2->cmdq, &cmd.cmd.hdr, + gp102_sec2_acr_bootstrap_falcon_callback, + &sec2->engine.subdev, + msecs_to_jiffies(1000)); +} + +static int +gp102_sec2_acr_boot(struct nvkm_falcon *falcon) +{ + struct nv_sec2_args args = {}; + nvkm_falcon_load_dmem(falcon, &args, + falcon->func->emem_addr, sizeof(args), 0); + nvkm_falcon_start(falcon); + return 0; +} + +static void +gp102_sec2_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust) +{ + struct loader_config_v1 hdr; + nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr)); + hdr.code_dma_base = hdr.code_dma_base + adjust; + hdr.data_dma_base = hdr.data_dma_base + adjust; + hdr.overlay_dma_base = hdr.overlay_dma_base + adjust; + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); + loader_config_v1_dump(&acr->subdev, &hdr); +} + +static void +gp102_sec2_acr_bld_write(struct nvkm_acr *acr, u32 bld, + struct nvkm_acr_lsfw *lsfw) +{ + const struct loader_config_v1 hdr = { + .dma_idx = FALCON_SEC2_DMAIDX_UCODE, + .code_dma_base = lsfw->offset.img + lsfw->app_start_offset, + .code_size_total = lsfw->app_size, + .code_size_to_load = lsfw->app_resident_code_size, + .code_entry_point = lsfw->app_imem_entry, + .data_dma_base = lsfw->offset.img + lsfw->app_start_offset + + lsfw->app_resident_data_offset, + .data_size = lsfw->app_resident_data_size, + .overlay_dma_base = lsfw->offset.img + lsfw->app_start_offset, + .argc = 1, + .argv = lsfw->falcon->func->emem_addr, + }; + + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); +} + +static const struct nvkm_acr_lsf_func +gp102_sec2_acr_0 = { + .bld_size = sizeof(struct loader_config_v1), + .bld_write = gp102_sec2_acr_bld_write, + .bld_patch = gp102_sec2_acr_bld_patch, + .boot = gp102_sec2_acr_boot, + .bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_FECS) | + BIT_ULL(NVKM_ACR_LSF_GPCCS) | + BIT_ULL(NVKM_ACR_LSF_SEC2), + .bootstrap_falcon = gp102_sec2_acr_bootstrap_falcon, +}; + +int +gp102_sec2_initmsg(struct nvkm_sec2 *sec2) +{ + struct nv_sec2_init_msg msg; + int ret, i; + + ret = nvkm_falcon_msgq_recv_initmsg(sec2->msgq, &msg, sizeof(msg)); + if (ret) + return ret; + + if (msg.hdr.unit_id != NV_SEC2_UNIT_INIT || + msg.msg_type != NV_SEC2_INIT_MSG_INIT) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(msg.queue_info); i++) { + if (msg.queue_info[i].id == NV_SEC2_INIT_MSG_QUEUE_ID_MSGQ) { + nvkm_falcon_msgq_init(sec2->msgq, + msg.queue_info[i].index, + msg.queue_info[i].offset, + msg.queue_info[i].size); + } else { + nvkm_falcon_cmdq_init(sec2->cmdq, + msg.queue_info[i].index, + msg.queue_info[i].offset, + msg.queue_info[i].size); + } + } + + return 0; +} + +void +gp102_sec2_intr(struct nvkm_sec2 *sec2) +{ + struct nvkm_subdev *subdev = &sec2->engine.subdev; + struct nvkm_falcon *falcon = &sec2->falcon; + u32 disp = nvkm_falcon_rd32(falcon, 0x01c); + u32 intr = nvkm_falcon_rd32(falcon, 0x008) & disp & ~(disp >> 16); + + if (intr & 0x00000040) { + schedule_work(&sec2->work); + nvkm_falcon_wr32(falcon, 0x004, 0x00000040); + intr &= ~0x00000040; + } + + if (intr) { + nvkm_error(subdev, "unhandled intr %08x\n", intr); + nvkm_falcon_wr32(falcon, 0x004, intr); + } +} + +int +gp102_sec2_flcn_enable(struct nvkm_falcon *falcon) +{ + nvkm_falcon_mask(falcon, 0x3c0, 0x00000001, 0x00000001); + udelay(10); + nvkm_falcon_mask(falcon, 0x3c0, 0x00000001, 0x00000000); + return nvkm_falcon_v1_enable(falcon); +} + +void +gp102_sec2_flcn_bind_context(struct nvkm_falcon *falcon, + struct nvkm_memory *ctx) +{ + struct nvkm_device *device = falcon->owner->device; + + nvkm_falcon_v1_bind_context(falcon, ctx); + if (!ctx) + return; + + /* Not sure if this is a WAR for a HW issue, or some additional + * programming sequence that's needed to properly complete the + * context switch we trigger above. + * + * Fixes unreliability of booting the SEC2 RTOS on Quadro P620, + * particularly when resuming from suspend. + * + * Also removes the need for an odd workaround where we needed + * to program SEC2's FALCON_CPUCTL_ALIAS_STARTCPU twice before + * the SEC2 RTOS would begin executing. + */ + nvkm_msec(device, 10, + u32 irqstat = nvkm_falcon_rd32(falcon, 0x008); + u32 flcn0dc = nvkm_falcon_rd32(falcon, 0x0dc); + if ((irqstat & 0x00000008) && + (flcn0dc & 0x00007000) == 0x00005000) + break; + ); + + nvkm_falcon_mask(falcon, 0x004, 0x00000008, 0x00000008); + nvkm_falcon_mask(falcon, 0x058, 0x00000002, 0x00000002); + + nvkm_msec(device, 10, + u32 flcn0dc = nvkm_falcon_rd32(falcon, 0x0dc); + if ((flcn0dc & 0x00007000) == 0x00000000) + break; + ); +} + +static const struct nvkm_falcon_func +gp102_sec2_flcn = { + .debug = 0x408, + .fbif = 0x600, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .emem_addr = 0x01000000, + .bind_context = gp102_sec2_flcn_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = gp102_sec2_flcn_enable, + .disable = nvkm_falcon_v1_disable, + .cmdq = { 0xa00, 0xa04, 8 }, + .msgq = { 0xa30, 0xa34, 8 }, +}; + +const struct nvkm_sec2_func +gp102_sec2 = { + .flcn = &gp102_sec2_flcn, + .unit_acr = NV_SEC2_UNIT_ACR, + .intr = gp102_sec2_intr, + .initmsg = gp102_sec2_initmsg, +}; + +MODULE_FIRMWARE("nvidia/gp102/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/gp102/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/gp102/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/sig.bin"); + +static void +gp102_sec2_acr_bld_patch_1(struct nvkm_acr *acr, u32 bld, s64 adjust) +{ + struct flcn_bl_dmem_desc_v2 hdr; + nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr)); + hdr.code_dma_base = hdr.code_dma_base + adjust; + hdr.data_dma_base = hdr.data_dma_base + adjust; + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); + flcn_bl_dmem_desc_v2_dump(&acr->subdev, &hdr); +} + +static void +gp102_sec2_acr_bld_write_1(struct nvkm_acr *acr, u32 bld, + struct nvkm_acr_lsfw *lsfw) +{ + const struct flcn_bl_dmem_desc_v2 hdr = { + .ctx_dma = FALCON_SEC2_DMAIDX_UCODE, + .code_dma_base = lsfw->offset.img + lsfw->app_start_offset, + .non_sec_code_off = lsfw->app_resident_code_offset, + .non_sec_code_size = lsfw->app_resident_code_size, + .code_entry_point = lsfw->app_imem_entry, + .data_dma_base = lsfw->offset.img + lsfw->app_start_offset + + lsfw->app_resident_data_offset, + .data_size = lsfw->app_resident_data_size, + .argc = 1, + .argv = lsfw->falcon->func->emem_addr, + }; + + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); +} + +const struct nvkm_acr_lsf_func +gp102_sec2_acr_1 = { + .bld_size = sizeof(struct flcn_bl_dmem_desc_v2), + .bld_write = gp102_sec2_acr_bld_write_1, + .bld_patch = gp102_sec2_acr_bld_patch_1, + .boot = gp102_sec2_acr_boot, + .bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_FECS) | + BIT_ULL(NVKM_ACR_LSF_GPCCS) | + BIT_ULL(NVKM_ACR_LSF_SEC2), + .bootstrap_falcon = gp102_sec2_acr_bootstrap_falcon, +}; + +int +gp102_sec2_load(struct nvkm_sec2 *sec2, int ver, + const struct nvkm_sec2_fwif *fwif) +{ + return nvkm_acr_lsfw_load_sig_image_desc_v1(&sec2->engine.subdev, + &sec2->falcon, + NVKM_ACR_LSF_SEC2, "sec2/", + ver, fwif->acr); +} + +MODULE_FIRMWARE("nvidia/gp102/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp102/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp102/sec2/sig-1.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/sig-1.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/sig-1.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/sig-1.bin"); + +static const struct nvkm_sec2_fwif +gp102_sec2_fwif[] = { + { 1, gp102_sec2_load, &gp102_sec2, &gp102_sec2_acr_1 }, + { 0, gp102_sec2_load, &gp102_sec2, &gp102_sec2_acr_0 }, + { -1, gp102_sec2_nofw, &gp102_sec2 }, + {} +}; + +int +gp102_sec2_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_sec2 **psec2) +{ + return nvkm_sec2_new_(gp102_sec2_fwif, device, type, inst, 0, psec2); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp108.c b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp108.c new file mode 100644 index 000000000..3e9f5c842 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp108.c @@ -0,0 +1,43 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include + +MODULE_FIRMWARE("nvidia/gp108/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/gp108/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/gp108/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gv100/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/gv100/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/gv100/sec2/sig.bin"); + +static const struct nvkm_sec2_fwif +gp108_sec2_fwif[] = { + { 0, gp102_sec2_load, &gp102_sec2, &gp102_sec2_acr_1 }, + {} +}; + +int +gp108_sec2_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_sec2 **psec2) +{ + return nvkm_sec2_new_(gp108_sec2_fwif, device, type, inst, 0, psec2); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h new file mode 100644 index 000000000..af19229e8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_SEC2_PRIV_H__ +#define __NVKM_SEC2_PRIV_H__ +#include + +struct nvkm_sec2_func { + const struct nvkm_falcon_func *flcn; + u8 unit_acr; + void (*intr)(struct nvkm_sec2 *); + int (*initmsg)(struct nvkm_sec2 *); +}; + +void gp102_sec2_intr(struct nvkm_sec2 *); +int gp102_sec2_initmsg(struct nvkm_sec2 *); + +struct nvkm_sec2_fwif { + int version; + int (*load)(struct nvkm_sec2 *, int ver, const struct nvkm_sec2_fwif *); + const struct nvkm_sec2_func *func; + const struct nvkm_acr_lsf_func *acr; +}; + +int gp102_sec2_nofw(struct nvkm_sec2 *, int, const struct nvkm_sec2_fwif *); +int gp102_sec2_load(struct nvkm_sec2 *, int, const struct nvkm_sec2_fwif *); +extern const struct nvkm_sec2_func gp102_sec2; +extern const struct nvkm_acr_lsf_func gp102_sec2_acr_1; + +int nvkm_sec2_new_(const struct nvkm_sec2_fwif *, struct nvkm_device *, enum nvkm_subdev_type, + int, u32 addr, struct nvkm_sec2 **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c new file mode 100644 index 000000000..f3faeb705 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c @@ -0,0 +1,82 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include + +static const struct nvkm_falcon_func +tu102_sec2_flcn = { + .debug = 0x408, + .fbif = 0x600, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .emem_addr = 0x01000000, + .bind_context = gp102_sec2_flcn_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = nvkm_falcon_v1_enable, + .disable = nvkm_falcon_v1_disable, + .cmdq = { 0xc00, 0xc04, 8 }, + .msgq = { 0xc80, 0xc84, 8 }, +}; + +static const struct nvkm_sec2_func +tu102_sec2 = { + .flcn = &tu102_sec2_flcn, + .unit_acr = 0x07, + .intr = gp102_sec2_intr, + .initmsg = gp102_sec2_initmsg, +}; + +MODULE_FIRMWARE("nvidia/tu102/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/tu102/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/tu102/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/tu104/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/tu104/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/tu104/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/tu106/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/tu106/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/tu106/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/tu116/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/tu116/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/tu116/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/tu117/sec2/desc.bin"); +MODULE_FIRMWARE("nvidia/tu117/sec2/image.bin"); +MODULE_FIRMWARE("nvidia/tu117/sec2/sig.bin"); + +static const struct nvkm_sec2_fwif +tu102_sec2_fwif[] = { + { 0, gp102_sec2_load, &tu102_sec2, &gp102_sec2_acr_1 }, + { -1, gp102_sec2_nofw, &tu102_sec2 } +}; + +int +tu102_sec2_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_sec2 **psec2) +{ + /* TOP info wasn't updated on Turing to reflect the PRI + * address change for some reason. We override it here. + */ + return nvkm_sec2_new_(tu102_sec2_fwif, device, type, inst, 0x840000, psec2); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild new file mode 100644 index 000000000..94fe25964 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/Kbuild @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/sw/base.o +nvkm-y += nvkm/engine/sw/nv04.o +nvkm-y += nvkm/engine/sw/nv10.o +nvkm-y += nvkm/engine/sw/nv50.o +nvkm-y += nvkm/engine/sw/gf100.o + +nvkm-y += nvkm/engine/sw/chan.o + +nvkm-y += nvkm/engine/sw/nvsw.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c new file mode 100644 index 000000000..14871d0bd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c @@ -0,0 +1,110 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" + +#include + +bool +nvkm_sw_mthd(struct nvkm_sw *sw, int chid, int subc, u32 mthd, u32 data) +{ + struct nvkm_sw_chan *chan; + bool handled = false; + unsigned long flags; + + spin_lock_irqsave(&sw->engine.lock, flags); + list_for_each_entry(chan, &sw->chan, head) { + if (chan->fifo->chid == chid) { + handled = nvkm_sw_chan_mthd(chan, subc, mthd, data); + list_del(&chan->head); + list_add(&chan->head, &sw->chan); + break; + } + } + spin_unlock_irqrestore(&sw->engine.lock, flags); + return handled; +} + +static int +nvkm_sw_oclass_new(const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_sw_chan *chan = nvkm_sw_chan(oclass->parent); + const struct nvkm_sw_chan_sclass *sclass = oclass->engn; + return sclass->ctor(chan, oclass, data, size, pobject); +} + +static int +nvkm_sw_oclass_get(struct nvkm_oclass *oclass, int index) +{ + struct nvkm_sw *sw = nvkm_sw(oclass->engine); + int c = 0; + + while (sw->func->sclass[c].ctor) { + if (c++ == index) { + oclass->engn = &sw->func->sclass[index]; + oclass->base = sw->func->sclass[index].base; + oclass->base.ctor = nvkm_sw_oclass_new; + return index; + } + } + + return c; +} + +static int +nvkm_sw_cclass_get(struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct nvkm_sw *sw = nvkm_sw(oclass->engine); + return sw->func->chan_new(sw, fifoch, oclass, pobject); +} + +static void * +nvkm_sw_dtor(struct nvkm_engine *engine) +{ + return nvkm_sw(engine); +} + +static const struct nvkm_engine_func +nvkm_sw = { + .dtor = nvkm_sw_dtor, + .fifo.cclass = nvkm_sw_cclass_get, + .fifo.sclass = nvkm_sw_oclass_get, +}; + +int +nvkm_sw_new_(const struct nvkm_sw_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_sw **psw) +{ + struct nvkm_sw *sw; + + if (!(sw = *psw = kzalloc(sizeof(*sw), GFP_KERNEL))) + return -ENOMEM; + INIT_LIST_HEAD(&sw->chan); + sw->func = func; + + return nvkm_engine_ctor(&nvkm_sw, device, type, inst, true, &sw->engine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c new file mode 100644 index 000000000..f28967065 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c @@ -0,0 +1,111 @@ +/* + * Copyright 2015 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 "chan.h" + +#include +#include + +#include +#include + +bool +nvkm_sw_chan_mthd(struct nvkm_sw_chan *chan, int subc, u32 mthd, u32 data) +{ + switch (mthd) { + case 0x0000: + return true; + case 0x0500: + nvkm_event_send(&chan->event, 1, 0, NULL, 0); + return true; + default: + if (chan->func->mthd) + return chan->func->mthd(chan, subc, mthd, data); + break; + } + return false; +} + +static int +nvkm_sw_chan_event_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + union { + struct nvif_notify_uevent_req none; + } *req = data; + int ret = -ENOSYS; + + if (!(ret = nvif_unvers(ret, &data, &size, req->none))) { + notify->size = sizeof(struct nvif_notify_uevent_rep); + notify->types = 1; + notify->index = 0; + } + + return ret; +} + +static const struct nvkm_event_func +nvkm_sw_chan_event = { + .ctor = nvkm_sw_chan_event_ctor, +}; + +static void * +nvkm_sw_chan_dtor(struct nvkm_object *object) +{ + struct nvkm_sw_chan *chan = nvkm_sw_chan(object); + struct nvkm_sw *sw = chan->sw; + unsigned long flags; + void *data = chan; + + if (chan->func->dtor) + data = chan->func->dtor(chan); + nvkm_event_fini(&chan->event); + + spin_lock_irqsave(&sw->engine.lock, flags); + list_del(&chan->head); + spin_unlock_irqrestore(&sw->engine.lock, flags); + return data; +} + +static const struct nvkm_object_func +nvkm_sw_chan = { + .dtor = nvkm_sw_chan_dtor, +}; + +int +nvkm_sw_chan_ctor(const struct nvkm_sw_chan_func *func, struct nvkm_sw *sw, + struct nvkm_fifo_chan *fifo, const struct nvkm_oclass *oclass, + struct nvkm_sw_chan *chan) +{ + unsigned long flags; + + nvkm_object_ctor(&nvkm_sw_chan, oclass, &chan->object); + chan->func = func; + chan->sw = sw; + chan->fifo = fifo; + spin_lock_irqsave(&sw->engine.lock, flags); + list_add(&chan->head, &sw->chan); + spin_unlock_irqrestore(&sw->engine.lock, flags); + + return nvkm_event_init(&nvkm_sw_chan_event, 1, 1, &chan->event); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h new file mode 100644 index 000000000..32de53427 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_SW_CHAN_H__ +#define __NVKM_SW_CHAN_H__ +#define nvkm_sw_chan(p) container_of((p), struct nvkm_sw_chan, object) +#include +#include + +#include "priv.h" + +struct nvkm_sw_chan { + const struct nvkm_sw_chan_func *func; + struct nvkm_object object; + struct nvkm_sw *sw; + struct nvkm_fifo_chan *fifo; + struct list_head head; + + struct nvkm_event event; +}; + +struct nvkm_sw_chan_func { + void *(*dtor)(struct nvkm_sw_chan *); + bool (*mthd)(struct nvkm_sw_chan *, int subc, u32 mthd, u32 data); +}; + +int nvkm_sw_chan_ctor(const struct nvkm_sw_chan_func *, struct nvkm_sw *, + struct nvkm_fifo_chan *, const struct nvkm_oclass *, + struct nvkm_sw_chan *); +bool nvkm_sw_chan_mthd(struct nvkm_sw_chan *, int subc, u32 mthd, u32 data); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c new file mode 100644 index 000000000..55abf839f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c @@ -0,0 +1,155 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include + +#include +#include + +/******************************************************************************* + * software context + ******************************************************************************/ + +static int +gf100_sw_chan_vblsem_release(struct nvkm_notify *notify) +{ + struct nv50_sw_chan *chan = + container_of(notify, typeof(*chan), vblank.notify[notify->index]); + struct nvkm_sw *sw = chan->base.sw; + struct nvkm_device *device = sw->engine.subdev.device; + u32 inst = chan->base.fifo->inst->addr >> 12; + + nvkm_wr32(device, 0x001718, 0x80000000 | inst); + nvkm_bar_flush(device->bar); + nvkm_wr32(device, 0x06000c, upper_32_bits(chan->vblank.offset)); + nvkm_wr32(device, 0x060010, lower_32_bits(chan->vblank.offset)); + nvkm_wr32(device, 0x060014, chan->vblank.value); + + return NVKM_NOTIFY_DROP; +} + +static bool +gf100_sw_chan_mthd(struct nvkm_sw_chan *base, int subc, u32 mthd, u32 data) +{ + struct nv50_sw_chan *chan = nv50_sw_chan(base); + struct nvkm_engine *engine = chan->base.object.engine; + struct nvkm_device *device = engine->subdev.device; + switch (mthd) { + case 0x0400: + chan->vblank.offset &= 0x00ffffffffULL; + chan->vblank.offset |= (u64)data << 32; + return true; + case 0x0404: + chan->vblank.offset &= 0xff00000000ULL; + chan->vblank.offset |= data; + return true; + case 0x0408: + chan->vblank.value = data; + return true; + case 0x040c: + if (data < device->disp->vblank.index_nr) { + nvkm_notify_get(&chan->vblank.notify[data]); + return true; + } + break; + case 0x600: /* MP.PM_UNK000 */ + nvkm_wr32(device, 0x419e00, data); + return true; + case 0x644: /* MP.TRAP_WARP_ERROR_EN */ + if (!(data & ~0x001ffffe)) { + nvkm_wr32(device, 0x419e44, data); + return true; + } + break; + case 0x6ac: /* MP.PM_UNK0AC */ + nvkm_wr32(device, 0x419eac, data); + return true; + default: + break; + } + return false; +} + +static const struct nvkm_sw_chan_func +gf100_sw_chan = { + .dtor = nv50_sw_chan_dtor, + .mthd = gf100_sw_chan_mthd, +}; + +static int +gf100_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, + struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = sw->engine.subdev.device->disp; + struct nv50_sw_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = nvkm_sw_chan_ctor(&gf100_sw_chan, sw, fifoch, oclass, + &chan->base); + if (ret) + return ret; + + for (i = 0; disp && i < disp->vblank.index_nr; i++) { + ret = nvkm_notify_init(NULL, &disp->vblank, + gf100_sw_chan_vblsem_release, false, + &(struct nvif_notify_head_req_v0) { + .head = i, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &chan->vblank.notify[i]); + if (ret) + return ret; + } + + return 0; +} + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_sw_func +gf100_sw = { + .chan_new = gf100_sw_chan_new, + .sclass = { + { nvkm_nvsw_new, { -1, -1, NVIF_CLASS_SW_GF100 } }, + {} + } +}; + +int +gf100_sw_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_sw **psw) +{ + return nvkm_sw_new_(&gf100_sw, device, type, inst, psw); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c new file mode 100644 index 000000000..4aa575738 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c @@ -0,0 +1,139 @@ +/* + * 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 + */ +#define nv04_sw_chan(p) container_of((p), struct nv04_sw_chan, base) +#include "priv.h" +#include "chan.h" +#include "nvsw.h" + +#include +#include +#include +#include + +struct nv04_sw_chan { + struct nvkm_sw_chan base; + atomic_t ref; +}; + +/******************************************************************************* + * software object classes + ******************************************************************************/ + +static int +nv04_nvsw_mthd_get_ref(struct nvkm_nvsw *nvsw, void *data, u32 size) +{ + struct nv04_sw_chan *chan = nv04_sw_chan(nvsw->chan); + union { + struct nv04_nvsw_get_ref_v0 v0; + } *args = data; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + args->v0.ref = atomic_read(&chan->ref); + } + + return ret; +} + +static int +nv04_nvsw_mthd(struct nvkm_nvsw *nvsw, u32 mthd, void *data, u32 size) +{ + switch (mthd) { + case NV04_NVSW_GET_REF: + return nv04_nvsw_mthd_get_ref(nvsw, data, size); + default: + break; + } + return -EINVAL; +} + +static const struct nvkm_nvsw_func +nv04_nvsw = { + .mthd = nv04_nvsw_mthd, +}; + +static int +nv04_nvsw_new(struct nvkm_sw_chan *chan, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + return nvkm_nvsw_new_(&nv04_nvsw, chan, oclass, data, size, pobject); +} + +/******************************************************************************* + * software context + ******************************************************************************/ + +static bool +nv04_sw_chan_mthd(struct nvkm_sw_chan *base, int subc, u32 mthd, u32 data) +{ + struct nv04_sw_chan *chan = nv04_sw_chan(base); + + switch (mthd) { + case 0x0150: + atomic_set(&chan->ref, data); + return true; + default: + break; + } + + return false; +} + +static const struct nvkm_sw_chan_func +nv04_sw_chan = { + .mthd = nv04_sw_chan_mthd, +}; + +static int +nv04_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifo, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nv04_sw_chan *chan; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + atomic_set(&chan->ref, 0); + *pobject = &chan->base.object; + + return nvkm_sw_chan_ctor(&nv04_sw_chan, sw, fifo, oclass, &chan->base); +} + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_sw_func +nv04_sw = { + .chan_new = nv04_sw_chan_new, + .sclass = { + { nv04_nvsw_new, { -1, -1, NVIF_CLASS_SW_NV04 } }, + {} + } +}; + +int +nv04_sw_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_sw **psw) +{ + return nvkm_sw_new_(&nv04_sw, device, type, inst, psw); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c new file mode 100644 index 000000000..e79e640ae --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c @@ -0,0 +1,68 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "chan.h" +#include "nvsw.h" + +#include + +/******************************************************************************* + * software context + ******************************************************************************/ + +static const struct nvkm_sw_chan_func +nv10_sw_chan = { +}; + +static int +nv10_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifo, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nvkm_sw_chan *chan; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->object; + + return nvkm_sw_chan_ctor(&nv10_sw_chan, sw, fifo, oclass, chan); +} + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_sw_func +nv10_sw = { + .chan_new = nv10_sw_chan_new, + .sclass = { + { nvkm_nvsw_new, { -1, -1, NVIF_CLASS_SW_NV10 } }, + {} + } +}; + +int +nv10_sw_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_sw **psw) +{ + return nvkm_sw_new_(&nv10_sw, device, type, inst, psw); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c new file mode 100644 index 000000000..1fdd094c8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c @@ -0,0 +1,148 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include + +#include +#include + +/******************************************************************************* + * software context + ******************************************************************************/ + +static int +nv50_sw_chan_vblsem_release(struct nvkm_notify *notify) +{ + struct nv50_sw_chan *chan = + container_of(notify, typeof(*chan), vblank.notify[notify->index]); + struct nvkm_sw *sw = chan->base.sw; + struct nvkm_device *device = sw->engine.subdev.device; + + nvkm_wr32(device, 0x001704, chan->base.fifo->inst->addr >> 12); + nvkm_wr32(device, 0x001710, 0x80000000 | chan->vblank.ctxdma); + nvkm_bar_flush(device->bar); + + if (device->chipset == 0x50) { + nvkm_wr32(device, 0x001570, chan->vblank.offset); + nvkm_wr32(device, 0x001574, chan->vblank.value); + } else { + nvkm_wr32(device, 0x060010, chan->vblank.offset); + nvkm_wr32(device, 0x060014, chan->vblank.value); + } + + return NVKM_NOTIFY_DROP; +} + +static bool +nv50_sw_chan_mthd(struct nvkm_sw_chan *base, int subc, u32 mthd, u32 data) +{ + struct nv50_sw_chan *chan = nv50_sw_chan(base); + struct nvkm_engine *engine = chan->base.object.engine; + struct nvkm_device *device = engine->subdev.device; + switch (mthd) { + case 0x018c: chan->vblank.ctxdma = data; return true; + case 0x0400: chan->vblank.offset = data; return true; + case 0x0404: chan->vblank.value = data; return true; + case 0x0408: + if (data < device->disp->vblank.index_nr) { + nvkm_notify_get(&chan->vblank.notify[data]); + return true; + } + break; + default: + break; + } + return false; +} + +void * +nv50_sw_chan_dtor(struct nvkm_sw_chan *base) +{ + struct nv50_sw_chan *chan = nv50_sw_chan(base); + int i; + for (i = 0; i < ARRAY_SIZE(chan->vblank.notify); i++) + nvkm_notify_fini(&chan->vblank.notify[i]); + return chan; +} + +static const struct nvkm_sw_chan_func +nv50_sw_chan = { + .dtor = nv50_sw_chan_dtor, + .mthd = nv50_sw_chan_mthd, +}; + +static int +nv50_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifoch, + const struct nvkm_oclass *oclass, struct nvkm_object **pobject) +{ + struct nvkm_disp *disp = sw->engine.subdev.device->disp; + struct nv50_sw_chan *chan; + int ret, i; + + if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) + return -ENOMEM; + *pobject = &chan->base.object; + + ret = nvkm_sw_chan_ctor(&nv50_sw_chan, sw, fifoch, oclass, &chan->base); + if (ret) + return ret; + + for (i = 0; disp && i < disp->vblank.index_nr; i++) { + ret = nvkm_notify_init(NULL, &disp->vblank, + nv50_sw_chan_vblsem_release, false, + &(struct nvif_notify_head_req_v0) { + .head = i, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &chan->vblank.notify[i]); + if (ret) + return ret; + } + + return 0; +} + +/******************************************************************************* + * software engine/subdev functions + ******************************************************************************/ + +static const struct nvkm_sw_func +nv50_sw = { + .chan_new = nv50_sw_chan_new, + .sclass = { + { nvkm_nvsw_new, { -1, -1, NVIF_CLASS_SW_NV50 } }, + {} + } +}; + +int +nv50_sw_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_sw **psw) +{ + return nvkm_sw_new_(&nv50_sw, device, type, inst, psw); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h new file mode 100644 index 000000000..6d364d7b4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_SW_NV50_H__ +#define __NVKM_SW_NV50_H__ +#define nv50_sw_chan(p) container_of((p), struct nv50_sw_chan, base) +#include "priv.h" +#include "chan.h" +#include "nvsw.h" +#include + +struct nv50_sw_chan { + struct nvkm_sw_chan base; + struct { + struct nvkm_notify notify[4]; + u32 ctxdma; + u64 offset; + u32 value; + } vblank; +}; + +void *nv50_sw_chan_dtor(struct nvkm_sw_chan *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.c new file mode 100644 index 000000000..33dd03fff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.c @@ -0,0 +1,85 @@ +/* + * Copyright 2015 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 "nvsw.h" +#include "chan.h" + +#include + +static int +nvkm_nvsw_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + struct nvkm_nvsw *nvsw = nvkm_nvsw(object); + if (nvsw->func->mthd) + return nvsw->func->mthd(nvsw, mthd, data, size); + return -ENODEV; +} + +static int +nvkm_nvsw_ntfy_(struct nvkm_object *object, u32 mthd, + struct nvkm_event **pevent) +{ + struct nvkm_nvsw *nvsw = nvkm_nvsw(object); + switch (mthd) { + case NV04_NVSW_NTFY_UEVENT: + *pevent = &nvsw->chan->event; + return 0; + default: + break; + } + return -EINVAL; +} + +static const struct nvkm_object_func +nvkm_nvsw_ = { + .mthd = nvkm_nvsw_mthd_, + .ntfy = nvkm_nvsw_ntfy_, +}; + +int +nvkm_nvsw_new_(const struct nvkm_nvsw_func *func, struct nvkm_sw_chan *chan, + const struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_nvsw *nvsw; + + if (!(nvsw = kzalloc(sizeof(*nvsw), GFP_KERNEL))) + return -ENOMEM; + *pobject = &nvsw->object; + + nvkm_object_ctor(&nvkm_nvsw_, oclass, &nvsw->object); + nvsw->func = func; + nvsw->chan = chan; + return 0; +} + +static const struct nvkm_nvsw_func +nvkm_nvsw = { +}; + +int +nvkm_nvsw_new(struct nvkm_sw_chan *chan, const struct nvkm_oclass *oclass, + void *data, u32 size, struct nvkm_object **pobject) +{ + return nvkm_nvsw_new_(&nvkm_nvsw, chan, oclass, data, size, pobject); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.h b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.h new file mode 100644 index 000000000..d2f846499 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_NVSW_H__ +#define __NVKM_NVSW_H__ +#define nvkm_nvsw(p) container_of((p), struct nvkm_nvsw, object) +#include + +struct nvkm_nvsw { + struct nvkm_object object; + const struct nvkm_nvsw_func *func; + struct nvkm_sw_chan *chan; +}; + +struct nvkm_nvsw_func { + int (*mthd)(struct nvkm_nvsw *, u32 mthd, void *data, u32 size); +}; + +int nvkm_nvsw_new_(const struct nvkm_nvsw_func *, struct nvkm_sw_chan *, + const struct nvkm_oclass *, void *data, u32 size, + struct nvkm_object **pobject); +int nvkm_nvsw_new(struct nvkm_sw_chan *, const struct nvkm_oclass *, + void *data, u32 size, struct nvkm_object **pobject); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h new file mode 100644 index 000000000..d9d83b1b8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_SW_PRIV_H__ +#define __NVKM_SW_PRIV_H__ +#define nvkm_sw(p) container_of((p), struct nvkm_sw, engine) +#include +struct nvkm_sw_chan; + +int nvkm_sw_new_(const struct nvkm_sw_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_sw **); + +struct nvkm_sw_chan_sclass { + int (*ctor)(struct nvkm_sw_chan *, const struct nvkm_oclass *, + void *data, u32 size, struct nvkm_object **); + struct nvkm_sclass base; +}; + +struct nvkm_sw_func { + int (*chan_new)(struct nvkm_sw *, struct nvkm_fifo_chan *, + const struct nvkm_oclass *, struct nvkm_object **); + const struct nvkm_sw_chan_sclass sclass[]; +}; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/vic/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/vic/Kbuild new file mode 100644 index 000000000..70164f482 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/vic/Kbuild @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: MIT +#nvkm-y += nvkm/engine/vic/base.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild new file mode 100644 index 000000000..d48ea0fd1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/vp/Kbuild @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/engine/vp/g84.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c new file mode 100644 index 000000000..b502266c7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/vp/g84.c @@ -0,0 +1,43 @@ +/* + * 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, Ilia Mirkin + */ +#include + +#include + +static const struct nvkm_xtensa_func +g84_vp = { + .fifo_val = 0x111, + .unkd28 = 0x9c544, + .sclass = { + { -1, -1, NV74_VP2 }, + {} + } +}; + +int +g84_vp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_engine **pengine) +{ + return nvkm_xtensa_new_(&g84_vp, device, type, inst, true, 0x00f000, pengine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c b/drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c new file mode 100644 index 000000000..f7d3ba0af --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/xtensa.c @@ -0,0 +1,191 @@ +/* + * Copyright 2013 Ilia Mirkin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include + +#include +#include + +static int +nvkm_xtensa_oclass_get(struct nvkm_oclass *oclass, int index) +{ + struct nvkm_xtensa *xtensa = nvkm_xtensa(oclass->engine); + int c = 0; + + while (xtensa->func->sclass[c].oclass) { + if (c++ == index) { + oclass->base = xtensa->func->sclass[index]; + return index; + } + } + + return c; +} + +static int +nvkm_xtensa_cclass_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent, + int align, struct nvkm_gpuobj **pgpuobj) +{ + return nvkm_gpuobj_new(object->engine->subdev.device, 0x10000, align, + true, parent, pgpuobj); +} + +static const struct nvkm_object_func +nvkm_xtensa_cclass = { + .bind = nvkm_xtensa_cclass_bind, +}; + +static void +nvkm_xtensa_intr(struct nvkm_engine *engine) +{ + struct nvkm_xtensa *xtensa = nvkm_xtensa(engine); + struct nvkm_subdev *subdev = &xtensa->engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 base = xtensa->addr; + u32 unk104 = nvkm_rd32(device, base + 0xd04); + u32 intr = nvkm_rd32(device, base + 0xc20); + u32 chan = nvkm_rd32(device, base + 0xc28); + u32 unk10c = nvkm_rd32(device, base + 0xd0c); + + if (intr & 0x10) + nvkm_warn(subdev, "Watchdog interrupt, engine hung.\n"); + nvkm_wr32(device, base + 0xc20, intr); + intr = nvkm_rd32(device, base + 0xc20); + if (unk104 == 0x10001 && unk10c == 0x200 && chan && !intr) { + nvkm_debug(subdev, "Enabling FIFO_CTRL\n"); + nvkm_mask(device, xtensa->addr + 0xd94, 0, xtensa->func->fifo_val); + } +} + +static int +nvkm_xtensa_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_xtensa *xtensa = nvkm_xtensa(engine); + struct nvkm_device *device = xtensa->engine.subdev.device; + const u32 base = xtensa->addr; + + nvkm_wr32(device, base + 0xd84, 0); /* INTR_EN */ + nvkm_wr32(device, base + 0xd94, 0); /* FIFO_CTRL */ + + if (!suspend) + nvkm_memory_unref(&xtensa->gpu_fw); + return 0; +} + +static int +nvkm_xtensa_init(struct nvkm_engine *engine) +{ + struct nvkm_xtensa *xtensa = nvkm_xtensa(engine); + struct nvkm_subdev *subdev = &xtensa->engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 base = xtensa->addr; + const struct firmware *fw; + char name[32]; + int i, ret; + u64 addr, size; + u32 tmp; + + if (!xtensa->gpu_fw) { + snprintf(name, sizeof(name), "nouveau/nv84_xuc%03x", + xtensa->addr >> 12); + + ret = request_firmware(&fw, name, device->dev); + if (ret) { + nvkm_warn(subdev, "unable to load firmware %s\n", name); + return ret; + } + + if (fw->size > 0x40000) { + nvkm_warn(subdev, "firmware %s too large\n", name); + release_firmware(fw); + return -EINVAL; + } + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, + 0x40000, 0x1000, false, + &xtensa->gpu_fw); + if (ret) { + release_firmware(fw); + return ret; + } + + nvkm_kmap(xtensa->gpu_fw); + for (i = 0; i < fw->size / 4; i++) + nvkm_wo32(xtensa->gpu_fw, i * 4, *((u32 *)fw->data + i)); + nvkm_done(xtensa->gpu_fw); + release_firmware(fw); + } + + addr = nvkm_memory_addr(xtensa->gpu_fw); + size = nvkm_memory_size(xtensa->gpu_fw); + + nvkm_wr32(device, base + 0xd10, 0x1fffffff); /* ?? */ + nvkm_wr32(device, base + 0xd08, 0x0fffffff); /* ?? */ + + nvkm_wr32(device, base + 0xd28, xtensa->func->unkd28); /* ?? */ + nvkm_wr32(device, base + 0xc20, 0x3f); /* INTR */ + nvkm_wr32(device, base + 0xd84, 0x3f); /* INTR_EN */ + + nvkm_wr32(device, base + 0xcc0, addr >> 8); /* XT_REGION_BASE */ + nvkm_wr32(device, base + 0xcc4, 0x1c); /* XT_REGION_SETUP */ + nvkm_wr32(device, base + 0xcc8, size >> 8); /* XT_REGION_LIMIT */ + + tmp = nvkm_rd32(device, 0x0); + nvkm_wr32(device, base + 0xde0, tmp); /* SCRATCH_H2X */ + + nvkm_wr32(device, base + 0xce8, 0xf); /* XT_REGION_SETUP */ + + nvkm_wr32(device, base + 0xc20, 0x3f); /* INTR */ + nvkm_wr32(device, base + 0xd84, 0x3f); /* INTR_EN */ + return 0; +} + +static void * +nvkm_xtensa_dtor(struct nvkm_engine *engine) +{ + return nvkm_xtensa(engine); +} + +static const struct nvkm_engine_func +nvkm_xtensa = { + .dtor = nvkm_xtensa_dtor, + .init = nvkm_xtensa_init, + .fini = nvkm_xtensa_fini, + .intr = nvkm_xtensa_intr, + .fifo.sclass = nvkm_xtensa_oclass_get, + .cclass = &nvkm_xtensa_cclass, +}; + +int +nvkm_xtensa_new_(const struct nvkm_xtensa_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool enable, u32 addr, + struct nvkm_engine **pengine) +{ + struct nvkm_xtensa *xtensa; + + if (!(xtensa = kzalloc(sizeof(*xtensa), GFP_KERNEL))) + return -ENOMEM; + xtensa->func = func; + xtensa->addr = addr; + *pengine = &xtensa->engine; + + return nvkm_engine_ctor(&nvkm_xtensa, device, type, inst, enable, &xtensa->engine); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild b/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild new file mode 100644 index 000000000..d79d78390 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/falcon/base.o +nvkm-y += nvkm/falcon/cmdq.o +nvkm-y += nvkm/falcon/msgq.o +nvkm-y += nvkm/falcon/qmgr.o +nvkm-y += nvkm/falcon/v1.o diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/base.c b/drivers/gpu/drm/nouveau/nvkm/falcon/base.c new file mode 100644 index 000000000..f3f90c106 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/base.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include + +void +nvkm_falcon_load_imem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u16 tag, u8 port, bool secure) +{ + if (secure && !falcon->secret) { + nvkm_warn(falcon->user, + "writing with secure tag on a non-secure falcon!\n"); + return; + } + + falcon->func->load_imem(falcon, data, start, size, tag, port, + secure); +} + +void +nvkm_falcon_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u8 port) +{ + mutex_lock(&falcon->dmem_mutex); + + falcon->func->load_dmem(falcon, data, start, size, port); + + mutex_unlock(&falcon->dmem_mutex); +} + +void +nvkm_falcon_read_dmem(struct nvkm_falcon *falcon, u32 start, u32 size, u8 port, + void *data) +{ + mutex_lock(&falcon->dmem_mutex); + + falcon->func->read_dmem(falcon, start, size, port, data); + + mutex_unlock(&falcon->dmem_mutex); +} + +void +nvkm_falcon_bind_context(struct nvkm_falcon *falcon, struct nvkm_memory *inst) +{ + if (!falcon->func->bind_context) { + nvkm_error(falcon->user, + "Context binding not supported on this falcon!\n"); + return; + } + + falcon->func->bind_context(falcon, inst); +} + +void +nvkm_falcon_set_start_addr(struct nvkm_falcon *falcon, u32 start_addr) +{ + falcon->func->set_start_addr(falcon, start_addr); +} + +void +nvkm_falcon_start(struct nvkm_falcon *falcon) +{ + falcon->func->start(falcon); +} + +int +nvkm_falcon_enable(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + nvkm_mc_enable(device, falcon->owner->type, falcon->owner->inst); + ret = falcon->func->enable(falcon); + if (ret) { + nvkm_mc_disable(device, falcon->owner->type, falcon->owner->inst); + return ret; + } + + return 0; +} + +void +nvkm_falcon_disable(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + + /* already disabled, return or wait_idle will timeout */ + if (!nvkm_mc_enabled(device, falcon->owner->type, falcon->owner->inst)) + return; + + falcon->func->disable(falcon); + + nvkm_mc_disable(device, falcon->owner->type, falcon->owner->inst); +} + +int +nvkm_falcon_reset(struct nvkm_falcon *falcon) +{ + if (!falcon->func->reset) { + nvkm_falcon_disable(falcon); + return nvkm_falcon_enable(falcon); + } + + return falcon->func->reset(falcon); +} + +int +nvkm_falcon_wait_for_halt(struct nvkm_falcon *falcon, u32 ms) +{ + return falcon->func->wait_for_halt(falcon, ms); +} + +int +nvkm_falcon_clear_interrupt(struct nvkm_falcon *falcon, u32 mask) +{ + return falcon->func->clear_interrupt(falcon, mask); +} + +static int +nvkm_falcon_oneinit(struct nvkm_falcon *falcon) +{ + const struct nvkm_falcon_func *func = falcon->func; + const struct nvkm_subdev *subdev = falcon->owner; + u32 reg; + + if (!falcon->addr) { + falcon->addr = nvkm_top_addr(subdev->device, subdev->type, subdev->inst); + if (WARN_ON(!falcon->addr)) + return -ENODEV; + } + + reg = nvkm_falcon_rd32(falcon, 0x12c); + falcon->version = reg & 0xf; + falcon->secret = (reg >> 4) & 0x3; + falcon->code.ports = (reg >> 8) & 0xf; + falcon->data.ports = (reg >> 12) & 0xf; + + reg = nvkm_falcon_rd32(falcon, 0x108); + falcon->code.limit = (reg & 0x1ff) << 8; + falcon->data.limit = (reg & 0x3fe00) >> 1; + + if (func->debug) { + u32 val = nvkm_falcon_rd32(falcon, func->debug); + falcon->debug = (val >> 20) & 0x1; + } + + return 0; +} + +void +nvkm_falcon_put(struct nvkm_falcon *falcon, const struct nvkm_subdev *user) +{ + if (unlikely(!falcon)) + return; + + mutex_lock(&falcon->mutex); + if (falcon->user == user) { + nvkm_debug(falcon->user, "released %s falcon\n", falcon->name); + falcon->user = NULL; + } + mutex_unlock(&falcon->mutex); +} + +int +nvkm_falcon_get(struct nvkm_falcon *falcon, const struct nvkm_subdev *user) +{ + int ret = 0; + + mutex_lock(&falcon->mutex); + if (falcon->user) { + nvkm_error(user, "%s falcon already acquired by %s!\n", + falcon->name, falcon->user->name); + mutex_unlock(&falcon->mutex); + return -EBUSY; + } + + nvkm_debug(user, "acquired %s falcon\n", falcon->name); + if (!falcon->oneinit) + ret = nvkm_falcon_oneinit(falcon); + falcon->user = user; + mutex_unlock(&falcon->mutex); + return ret; +} + +void +nvkm_falcon_dtor(struct nvkm_falcon *falcon) +{ +} + +int +nvkm_falcon_ctor(const struct nvkm_falcon_func *func, + struct nvkm_subdev *subdev, const char *name, u32 addr, + struct nvkm_falcon *falcon) +{ + falcon->func = func; + falcon->owner = subdev; + falcon->name = name; + falcon->addr = addr; + mutex_init(&falcon->mutex); + mutex_init(&falcon->dmem_mutex); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/cmdq.c b/drivers/gpu/drm/nouveau/nvkm/falcon/cmdq.c new file mode 100644 index 000000000..44cf6a886 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/cmdq.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "qmgr.h" + +static bool +nvkm_falcon_cmdq_has_room(struct nvkm_falcon_cmdq *cmdq, u32 size, bool *rewind) +{ + u32 head = nvkm_falcon_rd32(cmdq->qmgr->falcon, cmdq->head_reg); + u32 tail = nvkm_falcon_rd32(cmdq->qmgr->falcon, cmdq->tail_reg); + u32 free; + + size = ALIGN(size, QUEUE_ALIGNMENT); + + if (head >= tail) { + free = cmdq->offset + cmdq->size - head; + free -= HDR_SIZE; + + if (size > free) { + *rewind = true; + head = cmdq->offset; + } + } + + if (head < tail) + free = tail - head - 1; + + return size <= free; +} + +static void +nvkm_falcon_cmdq_push(struct nvkm_falcon_cmdq *cmdq, void *data, u32 size) +{ + struct nvkm_falcon *falcon = cmdq->qmgr->falcon; + nvkm_falcon_load_dmem(falcon, data, cmdq->position, size, 0); + cmdq->position += ALIGN(size, QUEUE_ALIGNMENT); +} + +static void +nvkm_falcon_cmdq_rewind(struct nvkm_falcon_cmdq *cmdq) +{ + struct nvfw_falcon_cmd cmd; + + cmd.unit_id = NV_FALCON_CMD_UNIT_ID_REWIND; + cmd.size = sizeof(cmd); + nvkm_falcon_cmdq_push(cmdq, &cmd, cmd.size); + + cmdq->position = cmdq->offset; +} + +static int +nvkm_falcon_cmdq_open(struct nvkm_falcon_cmdq *cmdq, u32 size) +{ + struct nvkm_falcon *falcon = cmdq->qmgr->falcon; + bool rewind = false; + + mutex_lock(&cmdq->mutex); + + if (!nvkm_falcon_cmdq_has_room(cmdq, size, &rewind)) { + FLCNQ_DBG(cmdq, "queue full"); + mutex_unlock(&cmdq->mutex); + return -EAGAIN; + } + + cmdq->position = nvkm_falcon_rd32(falcon, cmdq->head_reg); + + if (rewind) + nvkm_falcon_cmdq_rewind(cmdq); + + return 0; +} + +static void +nvkm_falcon_cmdq_close(struct nvkm_falcon_cmdq *cmdq) +{ + nvkm_falcon_wr32(cmdq->qmgr->falcon, cmdq->head_reg, cmdq->position); + mutex_unlock(&cmdq->mutex); +} + +static int +nvkm_falcon_cmdq_write(struct nvkm_falcon_cmdq *cmdq, struct nvfw_falcon_cmd *cmd) +{ + static unsigned timeout = 2000; + unsigned long end_jiffies = jiffies + msecs_to_jiffies(timeout); + int ret = -EAGAIN; + + while (ret == -EAGAIN && time_before(jiffies, end_jiffies)) + ret = nvkm_falcon_cmdq_open(cmdq, cmd->size); + if (ret) { + FLCNQ_ERR(cmdq, "timeout waiting for queue space"); + return ret; + } + + nvkm_falcon_cmdq_push(cmdq, cmd, cmd->size); + nvkm_falcon_cmdq_close(cmdq); + return ret; +} + +/* specifies that we want to know the command status in the answer message */ +#define CMD_FLAGS_STATUS BIT(0) +/* specifies that we want an interrupt when the answer message is queued */ +#define CMD_FLAGS_INTR BIT(1) + +int +nvkm_falcon_cmdq_send(struct nvkm_falcon_cmdq *cmdq, struct nvfw_falcon_cmd *cmd, + nvkm_falcon_qmgr_callback cb, void *priv, + unsigned long timeout) +{ + struct nvkm_falcon_qmgr_seq *seq; + int ret; + + if (!wait_for_completion_timeout(&cmdq->ready, + msecs_to_jiffies(1000))) { + FLCNQ_ERR(cmdq, "timeout waiting for queue ready"); + return -ETIMEDOUT; + } + + seq = nvkm_falcon_qmgr_seq_acquire(cmdq->qmgr); + if (IS_ERR(seq)) + return PTR_ERR(seq); + + cmd->seq_id = seq->id; + cmd->ctrl_flags = CMD_FLAGS_STATUS | CMD_FLAGS_INTR; + + seq->state = SEQ_STATE_USED; + seq->async = !timeout; + seq->callback = cb; + seq->priv = priv; + + ret = nvkm_falcon_cmdq_write(cmdq, cmd); + if (ret) { + seq->state = SEQ_STATE_PENDING; + nvkm_falcon_qmgr_seq_release(cmdq->qmgr, seq); + return ret; + } + + if (!seq->async) { + if (!wait_for_completion_timeout(&seq->done, timeout)) { + FLCNQ_ERR(cmdq, "timeout waiting for reply"); + return -ETIMEDOUT; + } + ret = seq->result; + nvkm_falcon_qmgr_seq_release(cmdq->qmgr, seq); + } + + return ret; +} + +void +nvkm_falcon_cmdq_fini(struct nvkm_falcon_cmdq *cmdq) +{ + reinit_completion(&cmdq->ready); +} + +void +nvkm_falcon_cmdq_init(struct nvkm_falcon_cmdq *cmdq, + u32 index, u32 offset, u32 size) +{ + const struct nvkm_falcon_func *func = cmdq->qmgr->falcon->func; + + cmdq->head_reg = func->cmdq.head + index * func->cmdq.stride; + cmdq->tail_reg = func->cmdq.tail + index * func->cmdq.stride; + cmdq->offset = offset; + cmdq->size = size; + complete_all(&cmdq->ready); + + FLCNQ_DBG(cmdq, "initialised @ index %d offset 0x%08x size 0x%08x", + index, cmdq->offset, cmdq->size); +} + +void +nvkm_falcon_cmdq_del(struct nvkm_falcon_cmdq **pcmdq) +{ + struct nvkm_falcon_cmdq *cmdq = *pcmdq; + if (cmdq) { + kfree(*pcmdq); + *pcmdq = NULL; + } +} + +int +nvkm_falcon_cmdq_new(struct nvkm_falcon_qmgr *qmgr, const char *name, + struct nvkm_falcon_cmdq **pcmdq) +{ + struct nvkm_falcon_cmdq *cmdq = *pcmdq; + + if (!(cmdq = *pcmdq = kzalloc(sizeof(*cmdq), GFP_KERNEL))) + return -ENOMEM; + + cmdq->qmgr = qmgr; + cmdq->name = name; + mutex_init(&cmdq->mutex); + init_completion(&cmdq->ready); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/msgq.c b/drivers/gpu/drm/nouveau/nvkm/falcon/msgq.c new file mode 100644 index 000000000..e74371dff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/msgq.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "qmgr.h" + +static void +nvkm_falcon_msgq_open(struct nvkm_falcon_msgq *msgq) +{ + mutex_lock(&msgq->mutex); + msgq->position = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->tail_reg); +} + +static void +nvkm_falcon_msgq_close(struct nvkm_falcon_msgq *msgq, bool commit) +{ + struct nvkm_falcon *falcon = msgq->qmgr->falcon; + + if (commit) + nvkm_falcon_wr32(falcon, msgq->tail_reg, msgq->position); + + mutex_unlock(&msgq->mutex); +} + +static bool +nvkm_falcon_msgq_empty(struct nvkm_falcon_msgq *msgq) +{ + u32 head = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->head_reg); + u32 tail = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->tail_reg); + return head == tail; +} + +static int +nvkm_falcon_msgq_pop(struct nvkm_falcon_msgq *msgq, void *data, u32 size) +{ + struct nvkm_falcon *falcon = msgq->qmgr->falcon; + u32 head, tail, available; + + head = nvkm_falcon_rd32(falcon, msgq->head_reg); + /* has the buffer looped? */ + if (head < msgq->position) + msgq->position = msgq->offset; + + tail = msgq->position; + + available = head - tail; + if (size > available) { + FLCNQ_ERR(msgq, "requested %d bytes, but only %d available", + size, available); + return -EINVAL; + } + + nvkm_falcon_read_dmem(falcon, tail, size, 0, data); + msgq->position += ALIGN(size, QUEUE_ALIGNMENT); + return 0; +} + +static int +nvkm_falcon_msgq_read(struct nvkm_falcon_msgq *msgq, struct nvfw_falcon_msg *hdr) +{ + int ret = 0; + + nvkm_falcon_msgq_open(msgq); + + if (nvkm_falcon_msgq_empty(msgq)) + goto close; + + ret = nvkm_falcon_msgq_pop(msgq, hdr, HDR_SIZE); + if (ret) { + FLCNQ_ERR(msgq, "failed to read message header"); + goto close; + } + + if (hdr->size > MSG_BUF_SIZE) { + FLCNQ_ERR(msgq, "message too big, %d bytes", hdr->size); + ret = -ENOSPC; + goto close; + } + + if (hdr->size > HDR_SIZE) { + u32 read_size = hdr->size - HDR_SIZE; + + ret = nvkm_falcon_msgq_pop(msgq, (hdr + 1), read_size); + if (ret) { + FLCNQ_ERR(msgq, "failed to read message data"); + goto close; + } + } + + ret = 1; +close: + nvkm_falcon_msgq_close(msgq, (ret >= 0)); + return ret; +} + +static int +nvkm_falcon_msgq_exec(struct nvkm_falcon_msgq *msgq, struct nvfw_falcon_msg *hdr) +{ + struct nvkm_falcon_qmgr_seq *seq; + + seq = &msgq->qmgr->seq.id[hdr->seq_id]; + if (seq->state != SEQ_STATE_USED && seq->state != SEQ_STATE_CANCELLED) { + FLCNQ_ERR(msgq, "message for unknown sequence %08x", seq->id); + return -EINVAL; + } + + if (seq->state == SEQ_STATE_USED) { + if (seq->callback) + seq->result = seq->callback(seq->priv, hdr); + } + + if (seq->async) { + nvkm_falcon_qmgr_seq_release(msgq->qmgr, seq); + return 0; + } + + complete_all(&seq->done); + return 0; +} + +void +nvkm_falcon_msgq_recv(struct nvkm_falcon_msgq *msgq) +{ + /* + * We are invoked from a worker thread, so normally we have plenty of + * stack space to work with. + */ + u8 msg_buffer[MSG_BUF_SIZE]; + struct nvfw_falcon_msg *hdr = (void *)msg_buffer; + + while (nvkm_falcon_msgq_read(msgq, hdr) > 0) + nvkm_falcon_msgq_exec(msgq, hdr); +} + +int +nvkm_falcon_msgq_recv_initmsg(struct nvkm_falcon_msgq *msgq, + void *data, u32 size) +{ + struct nvkm_falcon *falcon = msgq->qmgr->falcon; + struct nvfw_falcon_msg *hdr = data; + int ret; + + msgq->head_reg = falcon->func->msgq.head; + msgq->tail_reg = falcon->func->msgq.tail; + msgq->offset = nvkm_falcon_rd32(falcon, falcon->func->msgq.tail); + + nvkm_falcon_msgq_open(msgq); + ret = nvkm_falcon_msgq_pop(msgq, data, size); + if (ret == 0 && hdr->size != size) { + FLCN_ERR(falcon, "unexpected init message size %d vs %d", + hdr->size, size); + ret = -EINVAL; + } + nvkm_falcon_msgq_close(msgq, ret == 0); + return ret; +} + +void +nvkm_falcon_msgq_init(struct nvkm_falcon_msgq *msgq, + u32 index, u32 offset, u32 size) +{ + const struct nvkm_falcon_func *func = msgq->qmgr->falcon->func; + + msgq->head_reg = func->msgq.head + index * func->msgq.stride; + msgq->tail_reg = func->msgq.tail + index * func->msgq.stride; + msgq->offset = offset; + + FLCNQ_DBG(msgq, "initialised @ index %d offset 0x%08x size 0x%08x", + index, msgq->offset, size); +} + +void +nvkm_falcon_msgq_del(struct nvkm_falcon_msgq **pmsgq) +{ + struct nvkm_falcon_msgq *msgq = *pmsgq; + if (msgq) { + kfree(*pmsgq); + *pmsgq = NULL; + } +} + +int +nvkm_falcon_msgq_new(struct nvkm_falcon_qmgr *qmgr, const char *name, + struct nvkm_falcon_msgq **pmsgq) +{ + struct nvkm_falcon_msgq *msgq = *pmsgq; + + if (!(msgq = *pmsgq = kzalloc(sizeof(*msgq), GFP_KERNEL))) + return -ENOMEM; + + msgq->qmgr = qmgr; + msgq->name = name; + mutex_init(&msgq->mutex); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/priv.h b/drivers/gpu/drm/nouveau/nvkm/falcon/priv.h new file mode 100644 index 000000000..466188752 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/priv.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FALCON_PRIV_H__ +#define __NVKM_FALCON_PRIV_H__ +#include +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.c b/drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.c new file mode 100644 index 000000000..a453de341 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "qmgr.h" + +struct nvkm_falcon_qmgr_seq * +nvkm_falcon_qmgr_seq_acquire(struct nvkm_falcon_qmgr *qmgr) +{ + const struct nvkm_subdev *subdev = qmgr->falcon->owner; + struct nvkm_falcon_qmgr_seq *seq; + u32 index; + + mutex_lock(&qmgr->seq.mutex); + index = find_first_zero_bit(qmgr->seq.tbl, NVKM_FALCON_QMGR_SEQ_NUM); + if (index >= NVKM_FALCON_QMGR_SEQ_NUM) { + nvkm_error(subdev, "no free sequence available\n"); + mutex_unlock(&qmgr->seq.mutex); + return ERR_PTR(-EAGAIN); + } + + set_bit(index, qmgr->seq.tbl); + mutex_unlock(&qmgr->seq.mutex); + + seq = &qmgr->seq.id[index]; + seq->state = SEQ_STATE_PENDING; + return seq; +} + +void +nvkm_falcon_qmgr_seq_release(struct nvkm_falcon_qmgr *qmgr, + struct nvkm_falcon_qmgr_seq *seq) +{ + /* no need to acquire seq.mutex since clear_bit is atomic */ + seq->state = SEQ_STATE_FREE; + seq->callback = NULL; + reinit_completion(&seq->done); + clear_bit(seq->id, qmgr->seq.tbl); +} + +void +nvkm_falcon_qmgr_del(struct nvkm_falcon_qmgr **pqmgr) +{ + struct nvkm_falcon_qmgr *qmgr = *pqmgr; + if (qmgr) { + kfree(*pqmgr); + *pqmgr = NULL; + } +} + +int +nvkm_falcon_qmgr_new(struct nvkm_falcon *falcon, + struct nvkm_falcon_qmgr **pqmgr) +{ + struct nvkm_falcon_qmgr *qmgr; + int i; + + if (!(qmgr = *pqmgr = kzalloc(sizeof(*qmgr), GFP_KERNEL))) + return -ENOMEM; + + qmgr->falcon = falcon; + mutex_init(&qmgr->seq.mutex); + for (i = 0; i < NVKM_FALCON_QMGR_SEQ_NUM; i++) { + qmgr->seq.id[i].id = i; + init_completion(&qmgr->seq.id[i].done); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.h b/drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.h new file mode 100644 index 000000000..976cb7b7a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FALCON_QMGR_H__ +#define __NVKM_FALCON_QMGR_H__ +#include + +#define HDR_SIZE sizeof(struct nvfw_falcon_msg) +#define QUEUE_ALIGNMENT 4 +/* max size of the messages we can receive */ +#define MSG_BUF_SIZE 128 + +/** + * struct nvkm_falcon_qmgr_seq - keep track of ongoing commands + * + * Every time a command is sent, a sequence is assigned to it so the + * corresponding message can be matched. Upon receiving the message, a callback + * can be called and/or a completion signaled. + * + * @id: sequence ID + * @state: current state + * @callback: callback to call upon receiving matching message + * @completion: completion to signal after callback is called + */ +struct nvkm_falcon_qmgr_seq { + u16 id; + enum { + SEQ_STATE_FREE = 0, + SEQ_STATE_PENDING, + SEQ_STATE_USED, + SEQ_STATE_CANCELLED + } state; + bool async; + nvkm_falcon_qmgr_callback callback; + void *priv; + struct completion done; + int result; +}; + +/* + * We can have an arbitrary number of sequences, but realistically we will + * probably not use that much simultaneously. + */ +#define NVKM_FALCON_QMGR_SEQ_NUM 16 + +struct nvkm_falcon_qmgr { + struct nvkm_falcon *falcon; + + struct { + struct mutex mutex; + struct nvkm_falcon_qmgr_seq id[NVKM_FALCON_QMGR_SEQ_NUM]; + unsigned long tbl[BITS_TO_LONGS(NVKM_FALCON_QMGR_SEQ_NUM)]; + } seq; +}; + +struct nvkm_falcon_qmgr_seq * +nvkm_falcon_qmgr_seq_acquire(struct nvkm_falcon_qmgr *); +void nvkm_falcon_qmgr_seq_release(struct nvkm_falcon_qmgr *, + struct nvkm_falcon_qmgr_seq *); + +struct nvkm_falcon_cmdq { + struct nvkm_falcon_qmgr *qmgr; + const char *name; + struct mutex mutex; + struct completion ready; + + u32 head_reg; + u32 tail_reg; + u32 offset; + u32 size; + + u32 position; +}; + +struct nvkm_falcon_msgq { + struct nvkm_falcon_qmgr *qmgr; + const char *name; + struct mutex mutex; + + u32 head_reg; + u32 tail_reg; + u32 offset; + + u32 position; +}; + +#define FLCNQ_PRINTK(t,q,f,a...) \ + FLCN_PRINTK(t, (q)->qmgr->falcon, "%s: "f, (q)->name, ##a) +#define FLCNQ_DBG(q,f,a...) FLCNQ_PRINTK(debug, (q), f, ##a) +#define FLCNQ_ERR(q,f,a...) FLCNQ_PRINTK(error, (q), f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/v1.c b/drivers/gpu/drm/nouveau/nvkm/falcon/v1.c new file mode 100644 index 000000000..b0ee4c314 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/v1.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include + +void +nvkm_falcon_v1_load_imem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u16 tag, u8 port, bool secure) +{ + u8 rem = size % 4; + u32 reg; + int i; + + size -= rem; + + reg = start | BIT(24) | (secure ? BIT(28) : 0); + nvkm_falcon_wr32(falcon, 0x180 + (port * 16), reg); + for (i = 0; i < size / 4; i++) { + /* write new tag every 256B */ + if ((i & 0x3f) == 0) + nvkm_falcon_wr32(falcon, 0x188 + (port * 16), tag++); + nvkm_falcon_wr32(falcon, 0x184 + (port * 16), ((u32 *)data)[i]); + } + + /* + * If size is not a multiple of 4, mask the last work to ensure garbage + * does not get written + */ + if (rem) { + u32 extra = ((u32 *)data)[i]; + + /* write new tag every 256B */ + if ((i & 0x3f) == 0) + nvkm_falcon_wr32(falcon, 0x188 + (port * 16), tag++); + nvkm_falcon_wr32(falcon, 0x184 + (port * 16), + extra & (BIT(rem * 8) - 1)); + ++i; + } + + /* code must be padded to 0x40 words */ + for (; i & 0x3f; i++) + nvkm_falcon_wr32(falcon, 0x184 + (port * 16), 0); +} + +static void +nvkm_falcon_v1_load_emem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u8 port) +{ + u8 rem = size % 4; + int i; + + size -= rem; + + nvkm_falcon_wr32(falcon, 0xac0 + (port * 8), start | (0x1 << 24)); + for (i = 0; i < size / 4; i++) + nvkm_falcon_wr32(falcon, 0xac4 + (port * 8), ((u32 *)data)[i]); + + /* + * If size is not a multiple of 4, mask the last word to ensure garbage + * does not get written + */ + if (rem) { + u32 extra = ((u32 *)data)[i]; + + nvkm_falcon_wr32(falcon, 0xac4 + (port * 8), + extra & (BIT(rem * 8) - 1)); + } +} + +void +nvkm_falcon_v1_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u8 port) +{ + const struct nvkm_falcon_func *func = falcon->func; + u8 rem = size % 4; + int i; + + if (func->emem_addr && start >= func->emem_addr) + return nvkm_falcon_v1_load_emem(falcon, data, + start - func->emem_addr, size, + port); + + size -= rem; + + nvkm_falcon_wr32(falcon, 0x1c0 + (port * 8), start | (0x1 << 24)); + for (i = 0; i < size / 4; i++) + nvkm_falcon_wr32(falcon, 0x1c4 + (port * 8), ((u32 *)data)[i]); + + /* + * If size is not a multiple of 4, mask the last word to ensure garbage + * does not get written + */ + if (rem) { + u32 extra = ((u32 *)data)[i]; + + nvkm_falcon_wr32(falcon, 0x1c4 + (port * 8), + extra & (BIT(rem * 8) - 1)); + } +} + +static void +nvkm_falcon_v1_read_emem(struct nvkm_falcon *falcon, u32 start, u32 size, + u8 port, void *data) +{ + u8 rem = size % 4; + int i; + + size -= rem; + + nvkm_falcon_wr32(falcon, 0xac0 + (port * 8), start | (0x1 << 25)); + for (i = 0; i < size / 4; i++) + ((u32 *)data)[i] = nvkm_falcon_rd32(falcon, 0xac4 + (port * 8)); + + /* + * If size is not a multiple of 4, mask the last word to ensure garbage + * does not get read + */ + if (rem) { + u32 extra = nvkm_falcon_rd32(falcon, 0xac4 + (port * 8)); + + for (i = size; i < size + rem; i++) { + ((u8 *)data)[i] = (u8)(extra & 0xff); + extra >>= 8; + } + } +} + +void +nvkm_falcon_v1_read_dmem(struct nvkm_falcon *falcon, u32 start, u32 size, + u8 port, void *data) +{ + const struct nvkm_falcon_func *func = falcon->func; + u8 rem = size % 4; + int i; + + if (func->emem_addr && start >= func->emem_addr) + return nvkm_falcon_v1_read_emem(falcon, start - func->emem_addr, + size, port, data); + + size -= rem; + + nvkm_falcon_wr32(falcon, 0x1c0 + (port * 8), start | (0x1 << 25)); + for (i = 0; i < size / 4; i++) + ((u32 *)data)[i] = nvkm_falcon_rd32(falcon, 0x1c4 + (port * 8)); + + /* + * If size is not a multiple of 4, mask the last word to ensure garbage + * does not get read + */ + if (rem) { + u32 extra = nvkm_falcon_rd32(falcon, 0x1c4 + (port * 8)); + + for (i = size; i < size + rem; i++) { + ((u8 *)data)[i] = (u8)(extra & 0xff); + extra >>= 8; + } + } +} + +void +nvkm_falcon_v1_bind_context(struct nvkm_falcon *falcon, struct nvkm_memory *ctx) +{ + const u32 fbif = falcon->func->fbif; + u32 inst_loc; + + /* disable instance block binding */ + if (ctx == NULL) { + nvkm_falcon_wr32(falcon, 0x10c, 0x0); + return; + } + + nvkm_falcon_wr32(falcon, 0x10c, 0x1); + + /* setup apertures - virtual */ + nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_UCODE, 0x4); + nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_VIRT, 0x0); + /* setup apertures - physical */ + nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_PHYS_VID, 0x4); + nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_PHYS_SYS_COH, 0x5); + nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_PHYS_SYS_NCOH, 0x6); + + /* Set context */ + switch (nvkm_memory_target(ctx)) { + case NVKM_MEM_TARGET_VRAM: inst_loc = 0; break; + case NVKM_MEM_TARGET_HOST: inst_loc = 2; break; + case NVKM_MEM_TARGET_NCOH: inst_loc = 3; break; + default: + WARN_ON(1); + return; + } + + /* Enable context */ + nvkm_falcon_mask(falcon, 0x048, 0x1, 0x1); + nvkm_falcon_wr32(falcon, 0x054, + ((nvkm_memory_addr(ctx) >> 12) & 0xfffffff) | + (inst_loc << 28) | (1 << 30)); + + nvkm_falcon_mask(falcon, 0x090, 0x10000, 0x10000); + nvkm_falcon_mask(falcon, 0x0a4, 0x8, 0x8); +} + +void +nvkm_falcon_v1_set_start_addr(struct nvkm_falcon *falcon, u32 start_addr) +{ + nvkm_falcon_wr32(falcon, 0x104, start_addr); +} + +void +nvkm_falcon_v1_start(struct nvkm_falcon *falcon) +{ + u32 reg = nvkm_falcon_rd32(falcon, 0x100); + + if (reg & BIT(6)) + nvkm_falcon_wr32(falcon, 0x130, 0x2); + else + nvkm_falcon_wr32(falcon, 0x100, 0x2); +} + +int +nvkm_falcon_v1_wait_for_halt(struct nvkm_falcon *falcon, u32 ms) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + ret = nvkm_wait_msec(device, ms, falcon->addr + 0x100, 0x10, 0x10); + if (ret < 0) + return ret; + + return 0; +} + +int +nvkm_falcon_v1_clear_interrupt(struct nvkm_falcon *falcon, u32 mask) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + /* clear interrupt(s) */ + nvkm_falcon_mask(falcon, 0x004, mask, mask); + /* wait until interrupts are cleared */ + ret = nvkm_wait_msec(device, 10, falcon->addr + 0x008, mask, 0x0); + if (ret < 0) + return ret; + + return 0; +} + +static int +falcon_v1_wait_idle(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + ret = nvkm_wait_msec(device, 10, falcon->addr + 0x04c, 0xffff, 0x0); + if (ret < 0) + return ret; + + return 0; +} + +int +nvkm_falcon_v1_enable(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + ret = nvkm_wait_msec(device, 10, falcon->addr + 0x10c, 0x6, 0x0); + if (ret < 0) { + nvkm_error(falcon->user, "Falcon mem scrubbing timeout\n"); + return ret; + } + + ret = falcon_v1_wait_idle(falcon); + if (ret) + return ret; + + /* enable IRQs */ + nvkm_falcon_wr32(falcon, 0x010, 0xff); + + return 0; +} + +void +nvkm_falcon_v1_disable(struct nvkm_falcon *falcon) +{ + /* disable IRQs and wait for any previous code to complete */ + nvkm_falcon_wr32(falcon, 0x014, 0xff); + falcon_v1_wait_idle(falcon); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/nvfw/Kbuild b/drivers/gpu/drm/nouveau/nvkm/nvfw/Kbuild new file mode 100644 index 000000000..41d75f98e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/nvfw/Kbuild @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/nvfw/fw.o +nvkm-y += nvkm/nvfw/hs.o +nvkm-y += nvkm/nvfw/ls.o + +nvkm-y += nvkm/nvfw/acr.o +nvkm-y += nvkm/nvfw/flcn.o diff --git a/drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c b/drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c new file mode 100644 index 000000000..bef790ad8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c @@ -0,0 +1,164 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +void +wpr_header_dump(struct nvkm_subdev *subdev, const struct wpr_header *hdr) +{ + nvkm_debug(subdev, "wprHeader\n"); + nvkm_debug(subdev, "\tfalconID : %d\n", hdr->falcon_id); + nvkm_debug(subdev, "\tlsbOffset : 0x%x\n", hdr->lsb_offset); + nvkm_debug(subdev, "\tbootstrapOwner: %d\n", hdr->bootstrap_owner); + nvkm_debug(subdev, "\tlazyBootstrap : %d\n", hdr->lazy_bootstrap); + nvkm_debug(subdev, "\tstatus : %d\n", hdr->status); +} + +void +wpr_header_v1_dump(struct nvkm_subdev *subdev, const struct wpr_header_v1 *hdr) +{ + nvkm_debug(subdev, "wprHeader\n"); + nvkm_debug(subdev, "\tfalconID : %d\n", hdr->falcon_id); + nvkm_debug(subdev, "\tlsbOffset : 0x%x\n", hdr->lsb_offset); + nvkm_debug(subdev, "\tbootstrapOwner: %d\n", hdr->bootstrap_owner); + nvkm_debug(subdev, "\tlazyBootstrap : %d\n", hdr->lazy_bootstrap); + nvkm_debug(subdev, "\tbinVersion : %d\n", hdr->bin_version); + nvkm_debug(subdev, "\tstatus : %d\n", hdr->status); +} + +static void +lsb_header_tail_dump(struct nvkm_subdev *subdev, struct lsb_header_tail *hdr) +{ + nvkm_debug(subdev, "lsbHeader\n"); + nvkm_debug(subdev, "\tucodeOff : 0x%x\n", hdr->ucode_off); + nvkm_debug(subdev, "\tucodeSize : 0x%x\n", hdr->ucode_size); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); + nvkm_debug(subdev, "\tblCodeSize : 0x%x\n", hdr->bl_code_size); + nvkm_debug(subdev, "\tblImemOff : 0x%x\n", hdr->bl_imem_off); + nvkm_debug(subdev, "\tblDataOff : 0x%x\n", hdr->bl_data_off); + nvkm_debug(subdev, "\tblDataSize : 0x%x\n", hdr->bl_data_size); + nvkm_debug(subdev, "\tappCodeOff : 0x%x\n", hdr->app_code_off); + nvkm_debug(subdev, "\tappCodeSize : 0x%x\n", hdr->app_code_size); + nvkm_debug(subdev, "\tappDataOff : 0x%x\n", hdr->app_data_off); + nvkm_debug(subdev, "\tappDataSize : 0x%x\n", hdr->app_data_size); + nvkm_debug(subdev, "\tflags : 0x%x\n", hdr->flags); +} + +void +lsb_header_dump(struct nvkm_subdev *subdev, struct lsb_header *hdr) +{ + lsb_header_tail_dump(subdev, &hdr->tail); +} + +void +lsb_header_v1_dump(struct nvkm_subdev *subdev, struct lsb_header_v1 *hdr) +{ + lsb_header_tail_dump(subdev, &hdr->tail); +} + +void +flcn_acr_desc_dump(struct nvkm_subdev *subdev, struct flcn_acr_desc *hdr) +{ + int i; + + nvkm_debug(subdev, "acrDesc\n"); + nvkm_debug(subdev, "\twprRegionId : %d\n", hdr->wpr_region_id); + nvkm_debug(subdev, "\twprOffset : 0x%x\n", hdr->wpr_offset); + nvkm_debug(subdev, "\tmmuMemRange : 0x%x\n", + hdr->mmu_mem_range); + nvkm_debug(subdev, "\tnoRegions : %d\n", + hdr->regions.no_regions); + + for (i = 0; i < ARRAY_SIZE(hdr->regions.region_props); i++) { + nvkm_debug(subdev, "\tregion[%d] :\n", i); + nvkm_debug(subdev, "\t startAddr : 0x%x\n", + hdr->regions.region_props[i].start_addr); + nvkm_debug(subdev, "\t endAddr : 0x%x\n", + hdr->regions.region_props[i].end_addr); + nvkm_debug(subdev, "\t regionId : %d\n", + hdr->regions.region_props[i].region_id); + nvkm_debug(subdev, "\t readMask : 0x%x\n", + hdr->regions.region_props[i].read_mask); + nvkm_debug(subdev, "\t writeMask : 0x%x\n", + hdr->regions.region_props[i].write_mask); + nvkm_debug(subdev, "\t clientMask : 0x%x\n", + hdr->regions.region_props[i].client_mask); + } + + nvkm_debug(subdev, "\tucodeBlobSize: %d\n", + hdr->ucode_blob_size); + nvkm_debug(subdev, "\tucodeBlobBase: 0x%llx\n", + hdr->ucode_blob_base); + nvkm_debug(subdev, "\tvprEnabled : %d\n", + hdr->vpr_desc.vpr_enabled); + nvkm_debug(subdev, "\tvprStart : 0x%x\n", + hdr->vpr_desc.vpr_start); + nvkm_debug(subdev, "\tvprEnd : 0x%x\n", + hdr->vpr_desc.vpr_end); + nvkm_debug(subdev, "\thdcpPolicies : 0x%x\n", + hdr->vpr_desc.hdcp_policies); +} + +void +flcn_acr_desc_v1_dump(struct nvkm_subdev *subdev, struct flcn_acr_desc_v1 *hdr) +{ + int i; + + nvkm_debug(subdev, "acrDesc\n"); + nvkm_debug(subdev, "\twprRegionId : %d\n", hdr->wpr_region_id); + nvkm_debug(subdev, "\twprOffset : 0x%x\n", hdr->wpr_offset); + nvkm_debug(subdev, "\tmmuMemoryRange : 0x%x\n", + hdr->mmu_memory_range); + nvkm_debug(subdev, "\tnoRegions : %d\n", + hdr->regions.no_regions); + + for (i = 0; i < ARRAY_SIZE(hdr->regions.region_props); i++) { + nvkm_debug(subdev, "\tregion[%d] :\n", i); + nvkm_debug(subdev, "\t startAddr : 0x%x\n", + hdr->regions.region_props[i].start_addr); + nvkm_debug(subdev, "\t endAddr : 0x%x\n", + hdr->regions.region_props[i].end_addr); + nvkm_debug(subdev, "\t regionId : %d\n", + hdr->regions.region_props[i].region_id); + nvkm_debug(subdev, "\t readMask : 0x%x\n", + hdr->regions.region_props[i].read_mask); + nvkm_debug(subdev, "\t writeMask : 0x%x\n", + hdr->regions.region_props[i].write_mask); + nvkm_debug(subdev, "\t clientMask : 0x%x\n", + hdr->regions.region_props[i].client_mask); + nvkm_debug(subdev, "\t shadowMemStartAddr: 0x%x\n", + hdr->regions.region_props[i].shadow_mem_start_addr); + } + + nvkm_debug(subdev, "\tucodeBlobSize : %d\n", + hdr->ucode_blob_size); + nvkm_debug(subdev, "\tucodeBlobBase : 0x%llx\n", + hdr->ucode_blob_base); + nvkm_debug(subdev, "\tvprEnabled : %d\n", + hdr->vpr_desc.vpr_enabled); + nvkm_debug(subdev, "\tvprStart : 0x%x\n", + hdr->vpr_desc.vpr_start); + nvkm_debug(subdev, "\tvprEnd : 0x%x\n", + hdr->vpr_desc.vpr_end); + nvkm_debug(subdev, "\thdcpPolicies : 0x%x\n", + hdr->vpr_desc.hdcp_policies); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/nvfw/flcn.c b/drivers/gpu/drm/nouveau/nvkm/nvfw/flcn.c new file mode 100644 index 000000000..00ec764e1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/nvfw/flcn.c @@ -0,0 +1,115 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +void +loader_config_dump(struct nvkm_subdev *subdev, const struct loader_config *hdr) +{ + nvkm_debug(subdev, "loaderConfig\n"); + nvkm_debug(subdev, "\tdmaIdx : %d\n", hdr->dma_idx); + nvkm_debug(subdev, "\tcodeDmaBase : 0x%xx\n", hdr->code_dma_base); + nvkm_debug(subdev, "\tcodeSizeTotal : 0x%x\n", hdr->code_size_total); + nvkm_debug(subdev, "\tcodeSizeToLoad: 0x%x\n", hdr->code_size_to_load); + nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point); + nvkm_debug(subdev, "\tdataDmaBase : 0x%x\n", hdr->data_dma_base); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); + nvkm_debug(subdev, "\toverlayDmaBase: 0x%x\n", hdr->overlay_dma_base); + nvkm_debug(subdev, "\targc : 0x%08x\n", hdr->argc); + nvkm_debug(subdev, "\targv : 0x%08x\n", hdr->argv); + nvkm_debug(subdev, "\tcodeDmaBase1 : 0x%x\n", hdr->code_dma_base1); + nvkm_debug(subdev, "\tdataDmaBase1 : 0x%x\n", hdr->data_dma_base1); + nvkm_debug(subdev, "\tovlyDmaBase1 : 0x%x\n", hdr->overlay_dma_base1); +} + +void +loader_config_v1_dump(struct nvkm_subdev *subdev, + const struct loader_config_v1 *hdr) +{ + nvkm_debug(subdev, "loaderConfig\n"); + nvkm_debug(subdev, "\treserved : 0x%08x\n", hdr->reserved); + nvkm_debug(subdev, "\tdmaIdx : %d\n", hdr->dma_idx); + nvkm_debug(subdev, "\tcodeDmaBase : 0x%llxx\n", hdr->code_dma_base); + nvkm_debug(subdev, "\tcodeSizeTotal : 0x%x\n", hdr->code_size_total); + nvkm_debug(subdev, "\tcodeSizeToLoad: 0x%x\n", hdr->code_size_to_load); + nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point); + nvkm_debug(subdev, "\tdataDmaBase : 0x%llx\n", hdr->data_dma_base); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); + nvkm_debug(subdev, "\toverlayDmaBase: 0x%llx\n", hdr->overlay_dma_base); + nvkm_debug(subdev, "\targc : 0x%08x\n", hdr->argc); + nvkm_debug(subdev, "\targv : 0x%08x\n", hdr->argv); +} + +void +flcn_bl_dmem_desc_dump(struct nvkm_subdev *subdev, + const struct flcn_bl_dmem_desc *hdr) +{ + nvkm_debug(subdev, "flcnBlDmemDesc\n"); + nvkm_debug(subdev, "\treserved : 0x%08x 0x%08x 0x%08x 0x%08x\n", + hdr->reserved[0], hdr->reserved[1], hdr->reserved[2], + hdr->reserved[3]); + nvkm_debug(subdev, "\tsignature : 0x%08x 0x%08x 0x%08x 0x%08x\n", + hdr->signature[0], hdr->signature[1], hdr->signature[2], + hdr->signature[3]); + nvkm_debug(subdev, "\tctxDma : %d\n", hdr->ctx_dma); + nvkm_debug(subdev, "\tcodeDmaBase : 0x%x\n", hdr->code_dma_base); + nvkm_debug(subdev, "\tnonSecCodeOff : 0x%x\n", hdr->non_sec_code_off); + nvkm_debug(subdev, "\tnonSecCodeSize: 0x%x\n", hdr->non_sec_code_size); + nvkm_debug(subdev, "\tsecCodeOff : 0x%x\n", hdr->sec_code_off); + nvkm_debug(subdev, "\tsecCodeSize : 0x%x\n", hdr->sec_code_size); + nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point); + nvkm_debug(subdev, "\tdataDmaBase : 0x%x\n", hdr->data_dma_base); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); + nvkm_debug(subdev, "\tcodeDmaBase1 : 0x%x\n", hdr->code_dma_base1); + nvkm_debug(subdev, "\tdataDmaBase1 : 0x%x\n", hdr->data_dma_base1); +} + +void +flcn_bl_dmem_desc_v1_dump(struct nvkm_subdev *subdev, + const struct flcn_bl_dmem_desc_v1 *hdr) +{ + nvkm_debug(subdev, "flcnBlDmemDesc\n"); + nvkm_debug(subdev, "\treserved : 0x%08x 0x%08x 0x%08x 0x%08x\n", + hdr->reserved[0], hdr->reserved[1], hdr->reserved[2], + hdr->reserved[3]); + nvkm_debug(subdev, "\tsignature : 0x%08x 0x%08x 0x%08x 0x%08x\n", + hdr->signature[0], hdr->signature[1], hdr->signature[2], + hdr->signature[3]); + nvkm_debug(subdev, "\tctxDma : %d\n", hdr->ctx_dma); + nvkm_debug(subdev, "\tcodeDmaBase : 0x%llx\n", hdr->code_dma_base); + nvkm_debug(subdev, "\tnonSecCodeOff : 0x%x\n", hdr->non_sec_code_off); + nvkm_debug(subdev, "\tnonSecCodeSize: 0x%x\n", hdr->non_sec_code_size); + nvkm_debug(subdev, "\tsecCodeOff : 0x%x\n", hdr->sec_code_off); + nvkm_debug(subdev, "\tsecCodeSize : 0x%x\n", hdr->sec_code_size); + nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point); + nvkm_debug(subdev, "\tdataDmaBase : 0x%llx\n", hdr->data_dma_base); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); +} + +void +flcn_bl_dmem_desc_v2_dump(struct nvkm_subdev *subdev, + const struct flcn_bl_dmem_desc_v2 *hdr) +{ + flcn_bl_dmem_desc_v1_dump(subdev, (void *)hdr); + nvkm_debug(subdev, "\targc : 0x%08x\n", hdr->argc); + nvkm_debug(subdev, "\targv : 0x%08x\n", hdr->argv); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/nvfw/fw.c b/drivers/gpu/drm/nouveau/nvkm/nvfw/fw.c new file mode 100644 index 000000000..746803bd5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/nvfw/fw.c @@ -0,0 +1,51 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +const struct nvfw_bin_hdr * +nvfw_bin_hdr(struct nvkm_subdev *subdev, const void *data) +{ + const struct nvfw_bin_hdr *hdr = data; + nvkm_debug(subdev, "binHdr:\n"); + nvkm_debug(subdev, "\tbinMagic : 0x%08x\n", hdr->bin_magic); + nvkm_debug(subdev, "\tbinVer : %d\n", hdr->bin_ver); + nvkm_debug(subdev, "\tbinSize : %d\n", hdr->bin_size); + nvkm_debug(subdev, "\theaderOffset : 0x%x\n", hdr->header_offset); + nvkm_debug(subdev, "\tdataOffset : 0x%x\n", hdr->data_offset); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); + return hdr; +} + +const struct nvfw_bl_desc * +nvfw_bl_desc(struct nvkm_subdev *subdev, const void *data) +{ + const struct nvfw_bl_desc *hdr = data; + nvkm_debug(subdev, "blDesc\n"); + nvkm_debug(subdev, "\tstartTag : 0x%x\n", hdr->start_tag); + nvkm_debug(subdev, "\tdmemLoadOff : 0x%x\n", hdr->dmem_load_off); + nvkm_debug(subdev, "\tcodeOff : 0x%x\n", hdr->code_off); + nvkm_debug(subdev, "\tcodeSize : 0x%x\n", hdr->code_size); + nvkm_debug(subdev, "\tdataOff : 0x%x\n", hdr->data_off); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); + return hdr; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/nvfw/hs.c b/drivers/gpu/drm/nouveau/nvkm/nvfw/hs.c new file mode 100644 index 000000000..04ed77cb2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/nvfw/hs.c @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +const struct nvfw_hs_header * +nvfw_hs_header(struct nvkm_subdev *subdev, const void *data) +{ + const struct nvfw_hs_header *hdr = data; + nvkm_debug(subdev, "hsHeader:\n"); + nvkm_debug(subdev, "\tsigDbgOffset : 0x%x\n", hdr->sig_dbg_offset); + nvkm_debug(subdev, "\tsigDbgSize : 0x%x\n", hdr->sig_dbg_size); + nvkm_debug(subdev, "\tsigProdOffset : 0x%x\n", hdr->sig_prod_offset); + nvkm_debug(subdev, "\tsigProdSize : 0x%x\n", hdr->sig_prod_size); + nvkm_debug(subdev, "\tpatchLoc : 0x%x\n", hdr->patch_loc); + nvkm_debug(subdev, "\tpatchSig : 0x%x\n", hdr->patch_sig); + nvkm_debug(subdev, "\thdrOffset : 0x%x\n", hdr->hdr_offset); + nvkm_debug(subdev, "\thdrSize : 0x%x\n", hdr->hdr_size); + return hdr; +} + +const struct nvfw_hs_load_header * +nvfw_hs_load_header(struct nvkm_subdev *subdev, const void *data) +{ + const struct nvfw_hs_load_header *hdr = data; + int i; + + nvkm_debug(subdev, "hsLoadHeader:\n"); + nvkm_debug(subdev, "\tnonSecCodeOff : 0x%x\n", + hdr->non_sec_code_off); + nvkm_debug(subdev, "\tnonSecCodeSize : 0x%x\n", + hdr->non_sec_code_size); + nvkm_debug(subdev, "\tdataDmaBase : 0x%x\n", hdr->data_dma_base); + nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size); + nvkm_debug(subdev, "\tnumApps : 0x%x\n", hdr->num_apps); + for (i = 0; i < hdr->num_apps; i++) { + nvkm_debug(subdev, + "\tApp[%d] : offset 0x%x size 0x%x\n", i, + hdr->apps[(i * 2) + 0], hdr->apps[(i * 2) + 1]); + } + + return hdr; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c b/drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c new file mode 100644 index 000000000..b847f281c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c @@ -0,0 +1,108 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +static void +nvfw_ls_desc_head(struct nvkm_subdev *subdev, + const struct nvfw_ls_desc_head *hdr) +{ + char *date; + + nvkm_debug(subdev, "lsUcodeImgDesc:\n"); + nvkm_debug(subdev, "\tdescriptorSize : %d\n", + hdr->descriptor_size); + nvkm_debug(subdev, "\timageSize : %d\n", hdr->image_size); + nvkm_debug(subdev, "\ttoolsVersion : 0x%x\n", + hdr->tools_version); + nvkm_debug(subdev, "\tappVersion : 0x%x\n", hdr->app_version); + + date = kstrndup(hdr->date, sizeof(hdr->date), GFP_KERNEL); + nvkm_debug(subdev, "\tdate : %s\n", date); + kfree(date); + + nvkm_debug(subdev, "\tbootloaderStartOffset: 0x%x\n", + hdr->bootloader_start_offset); + nvkm_debug(subdev, "\tbootloaderSize : 0x%x\n", + hdr->bootloader_size); + nvkm_debug(subdev, "\tbootloaderImemOffset : 0x%x\n", + hdr->bootloader_imem_offset); + nvkm_debug(subdev, "\tbootloaderEntryPoint : 0x%x\n", + hdr->bootloader_entry_point); + + nvkm_debug(subdev, "\tappStartOffset : 0x%x\n", + hdr->app_start_offset); + nvkm_debug(subdev, "\tappSize : 0x%x\n", hdr->app_size); + nvkm_debug(subdev, "\tappImemOffset : 0x%x\n", + hdr->app_imem_offset); + nvkm_debug(subdev, "\tappImemEntry : 0x%x\n", + hdr->app_imem_entry); + nvkm_debug(subdev, "\tappDmemOffset : 0x%x\n", + hdr->app_dmem_offset); + nvkm_debug(subdev, "\tappResidentCodeOffset: 0x%x\n", + hdr->app_resident_code_offset); + nvkm_debug(subdev, "\tappResidentCodeSize : 0x%x\n", + hdr->app_resident_code_size); + nvkm_debug(subdev, "\tappResidentDataOffset: 0x%x\n", + hdr->app_resident_data_offset); + nvkm_debug(subdev, "\tappResidentDataSize : 0x%x\n", + hdr->app_resident_data_size); +} + +const struct nvfw_ls_desc * +nvfw_ls_desc(struct nvkm_subdev *subdev, const void *data) +{ + const struct nvfw_ls_desc *hdr = data; + int i; + + nvfw_ls_desc_head(subdev, &hdr->head); + + nvkm_debug(subdev, "\tnbOverlays : %d\n", hdr->nb_overlays); + for (i = 0; i < ARRAY_SIZE(hdr->load_ovl); i++) { + nvkm_debug(subdev, "\tloadOvl[%d] : 0x%x %d\n", i, + hdr->load_ovl[i].start, hdr->load_ovl[i].size); + } + nvkm_debug(subdev, "\tcompressed : %d\n", hdr->compressed); + + return hdr; +} + +const struct nvfw_ls_desc_v1 * +nvfw_ls_desc_v1(struct nvkm_subdev *subdev, const void *data) +{ + const struct nvfw_ls_desc_v1 *hdr = data; + int i; + + nvfw_ls_desc_head(subdev, &hdr->head); + + nvkm_debug(subdev, "\tnbImemOverlays : %d\n", + hdr->nb_imem_overlays); + nvkm_debug(subdev, "\tnbDmemOverlays : %d\n", + hdr->nb_imem_overlays); + for (i = 0; i < ARRAY_SIZE(hdr->load_ovl); i++) { + nvkm_debug(subdev, "\tloadOvl[%2d] : 0x%x %d\n", i, + hdr->load_ovl[i].start, hdr->load_ovl[i].size); + } + nvkm_debug(subdev, "\tcompressed : %d\n", hdr->compressed); + + return hdr; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild new file mode 100644 index 000000000..2cb24fff7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: MIT +include $(src)/nvkm/subdev/acr/Kbuild +include $(src)/nvkm/subdev/bar/Kbuild +include $(src)/nvkm/subdev/bios/Kbuild +include $(src)/nvkm/subdev/bus/Kbuild +include $(src)/nvkm/subdev/clk/Kbuild +include $(src)/nvkm/subdev/devinit/Kbuild +include $(src)/nvkm/subdev/fault/Kbuild +include $(src)/nvkm/subdev/fb/Kbuild +include $(src)/nvkm/subdev/fuse/Kbuild +include $(src)/nvkm/subdev/gpio/Kbuild +include $(src)/nvkm/subdev/gsp/Kbuild +include $(src)/nvkm/subdev/i2c/Kbuild +include $(src)/nvkm/subdev/iccsense/Kbuild +include $(src)/nvkm/subdev/instmem/Kbuild +include $(src)/nvkm/subdev/ltc/Kbuild +include $(src)/nvkm/subdev/mc/Kbuild +include $(src)/nvkm/subdev/mmu/Kbuild +include $(src)/nvkm/subdev/mxm/Kbuild +include $(src)/nvkm/subdev/pci/Kbuild +include $(src)/nvkm/subdev/pmu/Kbuild +include $(src)/nvkm/subdev/privring/Kbuild +include $(src)/nvkm/subdev/therm/Kbuild +include $(src)/nvkm/subdev/timer/Kbuild +include $(src)/nvkm/subdev/top/Kbuild +include $(src)/nvkm/subdev/volt/Kbuild diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild new file mode 100644 index 000000000..5b9f64a89 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/acr/base.o +nvkm-y += nvkm/subdev/acr/hsfw.o +nvkm-y += nvkm/subdev/acr/lsfw.o +nvkm-y += nvkm/subdev/acr/gm200.o +nvkm-y += nvkm/subdev/acr/gm20b.o +nvkm-y += nvkm/subdev/acr/gp102.o +nvkm-y += nvkm/subdev/acr/gp108.o +nvkm-y += nvkm/subdev/acr/gp10b.o +nvkm-y += nvkm/subdev/acr/tu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c new file mode 100644 index 000000000..af6cac696 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c @@ -0,0 +1,440 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include + +static struct nvkm_acr_hsf * +nvkm_acr_hsf_find(struct nvkm_acr *acr, const char *name) +{ + struct nvkm_acr_hsf *hsf; + list_for_each_entry(hsf, &acr->hsf, head) { + if (!strcmp(hsf->name, name)) + return hsf; + } + return NULL; +} + +int +nvkm_acr_hsf_boot(struct nvkm_acr *acr, const char *name) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_acr_hsf *hsf; + int ret; + + hsf = nvkm_acr_hsf_find(acr, name); + if (!hsf) + return -EINVAL; + + nvkm_debug(subdev, "executing %s binary\n", hsf->name); + ret = nvkm_falcon_get(hsf->falcon, subdev); + if (ret) + return ret; + + ret = hsf->func->boot(acr, hsf); + nvkm_falcon_put(hsf->falcon, subdev); + if (ret) { + nvkm_error(subdev, "%s binary failed\n", hsf->name); + return ret; + } + + nvkm_debug(subdev, "%s binary completed successfully\n", hsf->name); + return 0; +} + +static void +nvkm_acr_unload(struct nvkm_acr *acr) +{ + if (acr->done) { + nvkm_acr_hsf_boot(acr, "unload"); + acr->done = false; + } +} + +static int +nvkm_acr_load(struct nvkm_acr *acr) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_acr_lsf *lsf; + u64 start, limit; + int ret; + + if (list_empty(&acr->lsf)) { + nvkm_debug(subdev, "No LSF(s) present.\n"); + return 0; + } + + ret = acr->func->init(acr); + if (ret) + return ret; + + acr->func->wpr_check(acr, &start, &limit); + + if (start != acr->wpr_start || limit != acr->wpr_end) { + nvkm_error(subdev, "WPR not configured as expected: " + "%016llx-%016llx vs %016llx-%016llx\n", + acr->wpr_start, acr->wpr_end, start, limit); + return -EIO; + } + + acr->done = true; + + list_for_each_entry(lsf, &acr->lsf, head) { + if (lsf->func->boot) { + ret = lsf->func->boot(lsf->falcon); + if (ret) + break; + } + } + + return ret; +} + +static int +nvkm_acr_reload(struct nvkm_acr *acr) +{ + nvkm_acr_unload(acr); + return nvkm_acr_load(acr); +} + +static struct nvkm_acr_lsf * +nvkm_acr_falcon(struct nvkm_device *device) +{ + struct nvkm_acr *acr = device->acr; + struct nvkm_acr_lsf *lsf; + + if (acr) { + list_for_each_entry(lsf, &acr->lsf, head) { + if (lsf->func->bootstrap_falcon) + return lsf; + } + } + + return NULL; +} + +int +nvkm_acr_bootstrap_falcons(struct nvkm_device *device, unsigned long mask) +{ + struct nvkm_acr_lsf *acrflcn = nvkm_acr_falcon(device); + struct nvkm_acr *acr = device->acr; + unsigned long id; + + /* If there's no LS FW managing bootstrapping of other LS falcons, + * we depend on the HS firmware being able to do it instead. + */ + if (!acrflcn) { + /* Which isn't possible everywhere... */ + if ((mask & acr->func->bootstrap_falcons) == mask) { + int ret = nvkm_acr_reload(acr); + if (ret) + return ret; + + return acr->done ? 0 : -EINVAL; + } + return -ENOSYS; + } + + if ((mask & acrflcn->func->bootstrap_falcons) != mask) + return -ENOSYS; + + if (acrflcn->func->bootstrap_multiple_falcons) { + return acrflcn->func-> + bootstrap_multiple_falcons(acrflcn->falcon, mask); + } + + for_each_set_bit(id, &mask, NVKM_ACR_LSF_NUM) { + int ret = acrflcn->func->bootstrap_falcon(acrflcn->falcon, id); + if (ret) + return ret; + } + + return 0; +} + +bool +nvkm_acr_managed_falcon(struct nvkm_device *device, enum nvkm_acr_lsf_id id) +{ + struct nvkm_acr *acr = device->acr; + + if (acr) { + if (acr->managed_falcons & BIT_ULL(id)) + return true; + } + + return false; +} + +static int +nvkm_acr_fini(struct nvkm_subdev *subdev, bool suspend) +{ + nvkm_acr_unload(nvkm_acr(subdev)); + return 0; +} + +static int +nvkm_acr_init(struct nvkm_subdev *subdev) +{ + if (!nvkm_acr_falcon(subdev->device)) + return 0; + + return nvkm_acr_load(nvkm_acr(subdev)); +} + +static void +nvkm_acr_cleanup(struct nvkm_acr *acr) +{ + nvkm_acr_lsfw_del_all(acr); + nvkm_acr_hsfw_del_all(acr); + nvkm_firmware_put(acr->wpr_fw); + acr->wpr_fw = NULL; +} + +static int +nvkm_acr_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_device *device = subdev->device; + struct nvkm_acr *acr = nvkm_acr(subdev); + struct nvkm_acr_hsfw *hsfw; + struct nvkm_acr_lsfw *lsfw, *lsft; + struct nvkm_acr_lsf *lsf; + u32 wpr_size = 0; + u64 falcons; + int ret, i; + + if (list_empty(&acr->hsfw)) { + nvkm_debug(subdev, "No HSFW(s)\n"); + nvkm_acr_cleanup(acr); + return 0; + } + + /* Determine layout/size of WPR image up-front, as we need to know + * it to allocate memory before we begin constructing it. + */ + list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { + /* Cull unknown falcons that are present in WPR image. */ + if (acr->wpr_fw) { + if (!lsfw->func) { + nvkm_acr_lsfw_del(lsfw); + continue; + } + + wpr_size = acr->wpr_fw->size; + } + + /* Ensure we've fetched falcon configuration. */ + ret = nvkm_falcon_get(lsfw->falcon, subdev); + if (ret) + return ret; + + nvkm_falcon_put(lsfw->falcon, subdev); + + if (!(lsf = kmalloc(sizeof(*lsf), GFP_KERNEL))) + return -ENOMEM; + lsf->func = lsfw->func; + lsf->falcon = lsfw->falcon; + lsf->id = lsfw->id; + list_add_tail(&lsf->head, &acr->lsf); + acr->managed_falcons |= BIT_ULL(lsf->id); + } + + /* Ensure the falcon that'll provide ACR functions is booted first. */ + lsf = nvkm_acr_falcon(device); + if (lsf) { + falcons = lsf->func->bootstrap_falcons; + list_move(&lsf->head, &acr->lsf); + } else { + falcons = acr->func->bootstrap_falcons; + } + + /* Cull falcons that can't be bootstrapped, or the HSFW can fail to + * boot and leave the GPU in a weird state. + */ + list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { + if (!(falcons & BIT_ULL(lsfw->id))) { + nvkm_warn(subdev, "%s falcon cannot be bootstrapped\n", + nvkm_acr_lsf_id(lsfw->id)); + nvkm_acr_lsfw_del(lsfw); + } + } + + if (!acr->wpr_fw || acr->wpr_comp) + wpr_size = acr->func->wpr_layout(acr); + + /* Allocate/Locate WPR + fill ucode blob pointer. + * + * dGPU: allocate WPR + shadow blob + * Tegra: locate WPR with regs, ensure size is sufficient, + * allocate ucode blob. + */ + ret = acr->func->wpr_alloc(acr, wpr_size); + if (ret) + return ret; + + nvkm_debug(subdev, "WPR region is from 0x%llx-0x%llx (shadow 0x%llx)\n", + acr->wpr_start, acr->wpr_end, acr->shadow_start); + + /* Write WPR to ucode blob. */ + nvkm_kmap(acr->wpr); + if (acr->wpr_fw && !acr->wpr_comp) + nvkm_wobj(acr->wpr, 0, acr->wpr_fw->data, acr->wpr_fw->size); + + if (!acr->wpr_fw || acr->wpr_comp) + acr->func->wpr_build(acr, nvkm_acr_falcon(device)); + acr->func->wpr_patch(acr, (s64)acr->wpr_start - acr->wpr_prev); + + if (acr->wpr_fw && acr->wpr_comp) { + nvkm_kmap(acr->wpr); + for (i = 0; i < acr->wpr_fw->size; i += 4) { + u32 us = nvkm_ro32(acr->wpr, i); + u32 fw = ((u32 *)acr->wpr_fw->data)[i/4]; + if (fw != us) { + nvkm_warn(subdev, "%08x: %08x %08x\n", + i, us, fw); + } + } + return -EINVAL; + } + nvkm_done(acr->wpr); + + /* Allocate instance block for ACR-related stuff. */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0, true, + &acr->inst); + if (ret) + return ret; + + ret = nvkm_vmm_new(device, 0, 0, NULL, 0, NULL, "acr", &acr->vmm); + if (ret) + return ret; + + acr->vmm->debug = acr->subdev.debug; + + ret = nvkm_vmm_join(acr->vmm, acr->inst); + if (ret) + return ret; + + /* Load HS firmware blobs into ACR VMM. */ + list_for_each_entry(hsfw, &acr->hsfw, head) { + nvkm_debug(subdev, "loading %s fw\n", hsfw->name); + ret = hsfw->func->load(acr, hsfw); + if (ret) + return ret; + } + + /* Kill temporary data. */ + nvkm_acr_cleanup(acr); + return 0; +} + +static void * +nvkm_acr_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_acr *acr = nvkm_acr(subdev); + struct nvkm_acr_hsf *hsf, *hst; + struct nvkm_acr_lsf *lsf, *lst; + + list_for_each_entry_safe(hsf, hst, &acr->hsf, head) { + nvkm_vmm_put(acr->vmm, &hsf->vma); + nvkm_memory_unref(&hsf->ucode); + kfree(hsf->imem); + list_del(&hsf->head); + kfree(hsf); + } + + nvkm_vmm_part(acr->vmm, acr->inst); + nvkm_vmm_unref(&acr->vmm); + nvkm_memory_unref(&acr->inst); + + nvkm_memory_unref(&acr->wpr); + + list_for_each_entry_safe(lsf, lst, &acr->lsf, head) { + list_del(&lsf->head); + kfree(lsf); + } + + nvkm_acr_cleanup(acr); + return acr; +} + +static const struct nvkm_subdev_func +nvkm_acr = { + .dtor = nvkm_acr_dtor, + .oneinit = nvkm_acr_oneinit, + .init = nvkm_acr_init, + .fini = nvkm_acr_fini, +}; + +static int +nvkm_acr_ctor_wpr(struct nvkm_acr *acr, int ver) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_device *device = subdev->device; + int ret; + + ret = nvkm_firmware_get(subdev, "acr/wpr", ver, &acr->wpr_fw); + if (ret < 0) + return ret; + + /* Pre-add LSFs in the order they appear in the FW WPR image so that + * we're able to do a binary comparison with our own generator. + */ + ret = acr->func->wpr_parse(acr); + if (ret) + return ret; + + acr->wpr_comp = nvkm_boolopt(device->cfgopt, "NvAcrWprCompare", false); + acr->wpr_prev = nvkm_longopt(device->cfgopt, "NvAcrWprPrevAddr", 0); + return 0; +} + +int +nvkm_acr_new_(const struct nvkm_acr_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_acr **pacr) +{ + struct nvkm_acr *acr; + long wprfw; + + if (!(acr = *pacr = kzalloc(sizeof(*acr), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_acr, device, type, inst, &acr->subdev); + INIT_LIST_HEAD(&acr->hsfw); + INIT_LIST_HEAD(&acr->lsfw); + INIT_LIST_HEAD(&acr->hsf); + INIT_LIST_HEAD(&acr->lsf); + + fwif = nvkm_firmware_load(&acr->subdev, fwif, "Acr", acr); + if (IS_ERR(fwif)) + return PTR_ERR(fwif); + + acr->func = fwif->func; + + wprfw = nvkm_longopt(device->cfgopt, "NvAcrWpr", -1); + if (wprfw >= 0) { + int ret = nvkm_acr_ctor_wpr(acr, wprfw); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c new file mode 100644 index 000000000..82b4c8e14 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c @@ -0,0 +1,487 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +const struct nvkm_acr_func +gm200_acr = { +}; + +int +gm200_acr_nofw(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + nvkm_warn(&acr->subdev, "firmware unavailable\n"); + return 0; +} + +int +gm200_acr_init(struct nvkm_acr *acr) +{ + return nvkm_acr_hsf_boot(acr, "load"); +} + +void +gm200_acr_wpr_check(struct nvkm_acr *acr, u64 *start, u64 *limit) +{ + struct nvkm_device *device = acr->subdev.device; + + nvkm_wr32(device, 0x100cd4, 2); + *start = (u64)(nvkm_rd32(device, 0x100cd4) & 0xffffff00) << 8; + nvkm_wr32(device, 0x100cd4, 3); + *limit = (u64)(nvkm_rd32(device, 0x100cd4) & 0xffffff00) << 8; + *limit = *limit + 0x20000; +} + +void +gm200_acr_wpr_patch(struct nvkm_acr *acr, s64 adjust) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct wpr_header hdr; + struct lsb_header lsb; + struct nvkm_acr_lsf *lsfw; + u32 offset = 0; + + do { + nvkm_robj(acr->wpr, offset, &hdr, sizeof(hdr)); + wpr_header_dump(subdev, &hdr); + + list_for_each_entry(lsfw, &acr->lsfw, head) { + if (lsfw->id != hdr.falcon_id) + continue; + + nvkm_robj(acr->wpr, hdr.lsb_offset, &lsb, sizeof(lsb)); + lsb_header_dump(subdev, &lsb); + + lsfw->func->bld_patch(acr, lsb.tail.bl_data_off, adjust); + break; + } + offset += sizeof(hdr); + } while (hdr.falcon_id != WPR_HEADER_V0_FALCON_ID_INVALID); +} + +void +gm200_acr_wpr_build_lsb_tail(struct nvkm_acr_lsfw *lsfw, + struct lsb_header_tail *hdr) +{ + hdr->ucode_off = lsfw->offset.img; + hdr->ucode_size = lsfw->ucode_size; + hdr->data_size = lsfw->data_size; + hdr->bl_code_size = lsfw->bootloader_size; + hdr->bl_imem_off = lsfw->bootloader_imem_offset; + hdr->bl_data_off = lsfw->offset.bld; + hdr->bl_data_size = lsfw->bl_data_size; + hdr->app_code_off = lsfw->app_start_offset + + lsfw->app_resident_code_offset; + hdr->app_code_size = lsfw->app_resident_code_size; + hdr->app_data_off = lsfw->app_start_offset + + lsfw->app_resident_data_offset; + hdr->app_data_size = lsfw->app_resident_data_size; + hdr->flags = lsfw->func->flags; +} + +static int +gm200_acr_wpr_build_lsb(struct nvkm_acr *acr, struct nvkm_acr_lsfw *lsfw) +{ + struct lsb_header hdr; + + if (WARN_ON(lsfw->sig->size != sizeof(hdr.signature))) + return -EINVAL; + + memcpy(&hdr.signature, lsfw->sig->data, lsfw->sig->size); + gm200_acr_wpr_build_lsb_tail(lsfw, &hdr.tail); + + nvkm_wobj(acr->wpr, lsfw->offset.lsb, &hdr, sizeof(hdr)); + return 0; +} + +int +gm200_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos) +{ + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + int ret; + + /* Fill per-LSF structures. */ + list_for_each_entry(lsfw, &acr->lsfw, head) { + struct wpr_header hdr = { + .falcon_id = lsfw->id, + .lsb_offset = lsfw->offset.lsb, + .bootstrap_owner = NVKM_ACR_LSF_PMU, + .lazy_bootstrap = rtos && lsfw->id != rtos->id, + .status = WPR_HEADER_V0_STATUS_COPY, + }; + + /* Write WPR header. */ + nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr)); + offset += sizeof(hdr); + + /* Write LSB header. */ + ret = gm200_acr_wpr_build_lsb(acr, lsfw); + if (ret) + return ret; + + /* Write ucode image. */ + nvkm_wobj(acr->wpr, lsfw->offset.img, + lsfw->img.data, + lsfw->img.size); + + /* Write bootloader data. */ + lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw); + } + + /* Finalise WPR. */ + nvkm_wo32(acr->wpr, offset, WPR_HEADER_V0_FALCON_ID_INVALID); + return 0; +} + +static int +gm200_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size) +{ + int ret = nvkm_memory_new(acr->subdev.device, NVKM_MEM_TARGET_INST, + ALIGN(wpr_size, 0x40000), 0x40000, true, + &acr->wpr); + if (ret) + return ret; + + acr->wpr_start = nvkm_memory_addr(acr->wpr); + acr->wpr_end = acr->wpr_start + nvkm_memory_size(acr->wpr); + return 0; +} + +u32 +gm200_acr_wpr_layout(struct nvkm_acr *acr) +{ + struct nvkm_acr_lsfw *lsfw; + u32 wpr = 0; + + wpr += 11 /* MAX_LSF */ * sizeof(struct wpr_header); + + list_for_each_entry(lsfw, &acr->lsfw, head) { + wpr = ALIGN(wpr, 256); + lsfw->offset.lsb = wpr; + wpr += sizeof(struct lsb_header); + + wpr = ALIGN(wpr, 4096); + lsfw->offset.img = wpr; + wpr += lsfw->img.size; + + wpr = ALIGN(wpr, 256); + lsfw->offset.bld = wpr; + lsfw->bl_data_size = ALIGN(lsfw->func->bld_size, 256); + wpr += lsfw->bl_data_size; + } + + return wpr; +} + +int +gm200_acr_wpr_parse(struct nvkm_acr *acr) +{ + const struct wpr_header *hdr = (void *)acr->wpr_fw->data; + struct nvkm_acr_lsfw *lsfw; + + while (hdr->falcon_id != WPR_HEADER_V0_FALCON_ID_INVALID) { + wpr_header_dump(&acr->subdev, hdr); + lsfw = nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + } + + return 0; +} + +void +gm200_acr_hsfw_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + struct flcn_bl_dmem_desc_v1 hsdesc = { + .ctx_dma = FALCON_DMAIDX_VIRT, + .code_dma_base = hsf->vma->addr, + .non_sec_code_off = hsf->non_sec_addr, + .non_sec_code_size = hsf->non_sec_size, + .sec_code_off = hsf->sec_addr, + .sec_code_size = hsf->sec_size, + .code_entry_point = 0, + .data_dma_base = hsf->vma->addr + hsf->data_addr, + .data_size = hsf->data_size, + }; + + flcn_bl_dmem_desc_v1_dump(&acr->subdev, &hsdesc); + + nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0); +} + +int +gm200_acr_hsfw_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf, + u32 intr_clear, u32 mbox0_ok) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_falcon *falcon = hsf->falcon; + u32 mbox0, mbox1; + int ret; + + /* Reset falcon. */ + nvkm_falcon_reset(falcon); + nvkm_falcon_bind_context(falcon, acr->inst); + + /* Load bootloader into IMEM. */ + nvkm_falcon_load_imem(falcon, hsf->imem, + falcon->code.limit - hsf->imem_size, + hsf->imem_size, + hsf->imem_tag, + 0, false); + + /* Load bootloader data into DMEM. */ + hsf->func->bld(acr, hsf); + + /* Boot the falcon. */ + nvkm_mc_intr_mask(device, falcon->owner->type, falcon->owner->inst, false); + + nvkm_falcon_wr32(falcon, 0x040, 0xdeada5a5); + nvkm_falcon_set_start_addr(falcon, hsf->imem_tag << 8); + nvkm_falcon_start(falcon); + ret = nvkm_falcon_wait_for_halt(falcon, 100); + if (ret) + return ret; + + /* Check for successful completion. */ + mbox0 = nvkm_falcon_rd32(falcon, 0x040); + mbox1 = nvkm_falcon_rd32(falcon, 0x044); + nvkm_debug(subdev, "mailbox %08x %08x\n", mbox0, mbox1); + if (mbox0 && mbox0 != mbox0_ok) + return -EIO; + + nvkm_falcon_clear_interrupt(falcon, intr_clear); + nvkm_mc_intr_mask(device, falcon->owner->type, falcon->owner->inst, true); + return ret; +} + +int +gm200_acr_hsfw_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw, + struct nvkm_falcon *falcon) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_acr_hsf *hsf; + int ret; + + /* Patch the appropriate signature (production/debug) into the FW + * image, as determined by the mode the falcon is in. + */ + ret = nvkm_falcon_get(falcon, subdev); + if (ret) + return ret; + + if (hsfw->sig.patch_loc) { + if (!falcon->debug) { + nvkm_debug(subdev, "patching production signature\n"); + memcpy(hsfw->image + hsfw->sig.patch_loc, + hsfw->sig.prod.data, + hsfw->sig.prod.size); + } else { + nvkm_debug(subdev, "patching debug signature\n"); + memcpy(hsfw->image + hsfw->sig.patch_loc, + hsfw->sig.dbg.data, + hsfw->sig.dbg.size); + } + } + + nvkm_falcon_put(falcon, subdev); + + if (!(hsf = kzalloc(sizeof(*hsf), GFP_KERNEL))) + return -ENOMEM; + hsf->func = hsfw->func; + hsf->name = hsfw->name; + list_add_tail(&hsf->head, &acr->hsf); + + hsf->imem_size = hsfw->imem_size; + hsf->imem_tag = hsfw->imem_tag; + hsf->imem = kmemdup(hsfw->imem, hsfw->imem_size, GFP_KERNEL); + if (!hsf->imem) + return -ENOMEM; + + hsf->non_sec_addr = hsfw->non_sec_addr; + hsf->non_sec_size = hsfw->non_sec_size; + hsf->sec_addr = hsfw->sec_addr; + hsf->sec_size = hsfw->sec_size; + hsf->data_addr = hsfw->data_addr; + hsf->data_size = hsfw->data_size; + + /* Make the FW image accessible to the HS bootloader. */ + ret = nvkm_memory_new(subdev->device, NVKM_MEM_TARGET_INST, + hsfw->image_size, 0x1000, false, &hsf->ucode); + if (ret) + return ret; + + nvkm_kmap(hsf->ucode); + nvkm_wobj(hsf->ucode, 0, hsfw->image, hsfw->image_size); + nvkm_done(hsf->ucode); + + ret = nvkm_vmm_get(acr->vmm, 12, nvkm_memory_size(hsf->ucode), + &hsf->vma); + if (ret) + return ret; + + ret = nvkm_memory_map(hsf->ucode, 0, acr->vmm, hsf->vma, NULL, 0); + if (ret) + return ret; + + hsf->falcon = falcon; + return 0; +} + +int +gm200_acr_unload_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + return gm200_acr_hsfw_boot(acr, hsf, 0, 0x1d); +} + +int +gm200_acr_unload_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon); +} + +const struct nvkm_acr_hsf_func +gm200_acr_unload_0 = { + .load = gm200_acr_unload_load, + .boot = gm200_acr_unload_boot, + .bld = gm200_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gm200/acr/ucode_unload.bin"); +MODULE_FIRMWARE("nvidia/gm204/acr/ucode_unload.bin"); +MODULE_FIRMWARE("nvidia/gm206/acr/ucode_unload.bin"); +MODULE_FIRMWARE("nvidia/gp100/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +gm200_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm200_acr_unload_0 }, + {} +}; + +int +gm200_acr_load_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + return gm200_acr_hsfw_boot(acr, hsf, 0x10, 0); +} + +static int +gm200_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + struct flcn_acr_desc *desc = (void *)&hsfw->image[hsfw->data_addr]; + + desc->wpr_region_id = 1; + desc->regions.no_regions = 2; + desc->regions.region_props[0].start_addr = acr->wpr_start >> 8; + desc->regions.region_props[0].end_addr = acr->wpr_end >> 8; + desc->regions.region_props[0].region_id = 1; + desc->regions.region_props[0].read_mask = 0xf; + desc->regions.region_props[0].write_mask = 0xc; + desc->regions.region_props[0].client_mask = 0x2; + flcn_acr_desc_dump(&acr->subdev, desc); + + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon); +} + +static const struct nvkm_acr_hsf_func +gm200_acr_load_0 = { + .load = gm200_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gm200_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gm200/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm200/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gm204/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm204/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gm206/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm206/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp100/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp100/acr/ucode_load.bin"); + +static const struct nvkm_acr_hsf_fwif +gm200_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm200_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gm200_acr_0 = { + .load = gm200_acr_load_fwif, + .unload = gm200_acr_unload_fwif, + .wpr_parse = gm200_acr_wpr_parse, + .wpr_layout = gm200_acr_wpr_layout, + .wpr_alloc = gm200_acr_wpr_alloc, + .wpr_build = gm200_acr_wpr_build, + .wpr_patch = gm200_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, + .bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_FECS) | + BIT_ULL(NVKM_ACR_LSF_GPCCS), +}; + +static int +gm200_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad", + acr, "acr/bl", "acr/ucode_load", "load"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload", + acr, "acr/bl", "acr/ucode_unload", + "unload"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +gm200_acr_fwif[] = { + { 0, gm200_acr_load, &gm200_acr_0 }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gm200_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gm200_acr_fwif, device, type, inst, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c new file mode 100644 index 000000000..54e996f2f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c @@ -0,0 +1,136 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include +#include + +#include +#include + +int +gm20b_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size) +{ + struct nvkm_subdev *subdev = &acr->subdev; + + acr->func->wpr_check(acr, &acr->wpr_start, &acr->wpr_end); + + if ((acr->wpr_end - acr->wpr_start) < wpr_size) { + nvkm_error(subdev, "WPR image too big for WPR!\n"); + return -ENOSPC; + } + + return nvkm_memory_new(subdev->device, NVKM_MEM_TARGET_INST, + wpr_size, 0, true, &acr->wpr); +} + +static void +gm20b_acr_load_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + struct flcn_bl_dmem_desc hsdesc = { + .ctx_dma = FALCON_DMAIDX_VIRT, + .code_dma_base = hsf->vma->addr >> 8, + .non_sec_code_off = hsf->non_sec_addr, + .non_sec_code_size = hsf->non_sec_size, + .sec_code_off = hsf->sec_addr, + .sec_code_size = hsf->sec_size, + .code_entry_point = 0, + .data_dma_base = (hsf->vma->addr + hsf->data_addr) >> 8, + .data_size = hsf->data_size, + }; + + flcn_bl_dmem_desc_dump(&acr->subdev, &hsdesc); + + nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0); +} + +static int +gm20b_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + struct flcn_acr_desc *desc = (void *)&hsfw->image[hsfw->data_addr]; + + desc->ucode_blob_base = nvkm_memory_addr(acr->wpr); + desc->ucode_blob_size = nvkm_memory_size(acr->wpr); + flcn_acr_desc_dump(&acr->subdev, desc); + + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon); +} + +const struct nvkm_acr_hsf_func +gm20b_acr_load_0 = { + .load = gm20b_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gm20b_acr_load_bld, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +MODULE_FIRMWARE("nvidia/gm20b/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm20b/acr/ucode_load.bin"); +#endif + +static const struct nvkm_acr_hsf_fwif +gm20b_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm20b_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gm20b_acr = { + .load = gm20b_acr_load_fwif, + .wpr_parse = gm200_acr_wpr_parse, + .wpr_layout = gm200_acr_wpr_layout, + .wpr_alloc = gm20b_acr_wpr_alloc, + .wpr_build = gm200_acr_wpr_build, + .wpr_patch = gm200_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +int +gm20b_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad", + acr, "acr/bl", "acr/ucode_load", "load"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +gm20b_acr_fwif[] = { + { 0, gm20b_acr_load, &gm20b_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gm20b_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gm20b_acr_fwif, device, type, inst, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c new file mode 100644 index 000000000..fd97a935a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c @@ -0,0 +1,285 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include +#include + +#include +#include + +void +gp102_acr_wpr_patch(struct nvkm_acr *acr, s64 adjust) +{ + struct wpr_header_v1 hdr; + struct lsb_header_v1 lsb; + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + + do { + nvkm_robj(acr->wpr, offset, &hdr, sizeof(hdr)); + wpr_header_v1_dump(&acr->subdev, &hdr); + + list_for_each_entry(lsfw, &acr->lsfw, head) { + if (lsfw->id != hdr.falcon_id) + continue; + + nvkm_robj(acr->wpr, hdr.lsb_offset, &lsb, sizeof(lsb)); + lsb_header_v1_dump(&acr->subdev, &lsb); + + lsfw->func->bld_patch(acr, lsb.tail.bl_data_off, adjust); + break; + } + + offset += sizeof(hdr); + } while (hdr.falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID); +} + +int +gp102_acr_wpr_build_lsb(struct nvkm_acr *acr, struct nvkm_acr_lsfw *lsfw) +{ + struct lsb_header_v1 hdr; + + if (WARN_ON(lsfw->sig->size != sizeof(hdr.signature))) + return -EINVAL; + + memcpy(&hdr.signature, lsfw->sig->data, lsfw->sig->size); + gm200_acr_wpr_build_lsb_tail(lsfw, &hdr.tail); + + nvkm_wobj(acr->wpr, lsfw->offset.lsb, &hdr, sizeof(hdr)); + return 0; +} + +int +gp102_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos) +{ + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + int ret; + + /* Fill per-LSF structures. */ + list_for_each_entry(lsfw, &acr->lsfw, head) { + struct lsf_signature_v1 *sig = (void *)lsfw->sig->data; + struct wpr_header_v1 hdr = { + .falcon_id = lsfw->id, + .lsb_offset = lsfw->offset.lsb, + .bootstrap_owner = NVKM_ACR_LSF_SEC2, + .lazy_bootstrap = rtos && lsfw->id != rtos->id, + .bin_version = sig->version, + .status = WPR_HEADER_V1_STATUS_COPY, + }; + + /* Write WPR header. */ + nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr)); + offset += sizeof(hdr); + + /* Write LSB header. */ + ret = gp102_acr_wpr_build_lsb(acr, lsfw); + if (ret) + return ret; + + /* Write ucode image. */ + nvkm_wobj(acr->wpr, lsfw->offset.img, + lsfw->img.data, + lsfw->img.size); + + /* Write bootloader data. */ + lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw); + } + + /* Finalise WPR. */ + nvkm_wo32(acr->wpr, offset, WPR_HEADER_V1_FALCON_ID_INVALID); + return 0; +} + +int +gp102_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size) +{ + int ret = nvkm_memory_new(acr->subdev.device, NVKM_MEM_TARGET_INST, + ALIGN(wpr_size, 0x40000) << 1, 0x40000, true, + &acr->wpr); + if (ret) + return ret; + + acr->shadow_start = nvkm_memory_addr(acr->wpr); + acr->wpr_start = acr->shadow_start + (nvkm_memory_size(acr->wpr) >> 1); + acr->wpr_end = acr->wpr_start + (nvkm_memory_size(acr->wpr) >> 1); + return 0; +} + +u32 +gp102_acr_wpr_layout(struct nvkm_acr *acr) +{ + struct nvkm_acr_lsfw *lsfw; + u32 wpr = 0; + + wpr += 11 /* MAX_LSF */ * sizeof(struct wpr_header_v1); + wpr = ALIGN(wpr, 256); + + wpr += 0x100; /* Shared sub-WPR headers. */ + + list_for_each_entry(lsfw, &acr->lsfw, head) { + wpr = ALIGN(wpr, 256); + lsfw->offset.lsb = wpr; + wpr += sizeof(struct lsb_header_v1); + + wpr = ALIGN(wpr, 4096); + lsfw->offset.img = wpr; + wpr += lsfw->img.size; + + wpr = ALIGN(wpr, 256); + lsfw->offset.bld = wpr; + lsfw->bl_data_size = ALIGN(lsfw->func->bld_size, 256); + wpr += lsfw->bl_data_size; + } + + return wpr; +} + +int +gp102_acr_wpr_parse(struct nvkm_acr *acr) +{ + const struct wpr_header_v1 *hdr = (void *)acr->wpr_fw->data; + struct nvkm_acr_lsfw *lsfw; + + while (hdr->falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID) { + wpr_header_v1_dump(&acr->subdev, hdr); + lsfw = nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + } + + return 0; +} + +MODULE_FIRMWARE("nvidia/gp102/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp102/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gp104/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp104/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gp106/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp106/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gp107/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp107/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +gp102_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm200_acr_unload_0 }, + {} +}; + +int +gp102_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + struct flcn_acr_desc_v1 *desc = (void *)&hsfw->image[hsfw->data_addr]; + + desc->wpr_region_id = 1; + desc->regions.no_regions = 2; + desc->regions.region_props[0].start_addr = acr->wpr_start >> 8; + desc->regions.region_props[0].end_addr = acr->wpr_end >> 8; + desc->regions.region_props[0].region_id = 1; + desc->regions.region_props[0].read_mask = 0xf; + desc->regions.region_props[0].write_mask = 0xc; + desc->regions.region_props[0].client_mask = 0x2; + desc->regions.region_props[0].shadow_mem_start_addr = + acr->shadow_start >> 8; + flcn_acr_desc_v1_dump(&acr->subdev, desc); + + return gm200_acr_hsfw_load(acr, hsfw, + &acr->subdev.device->sec2->falcon); +} + +static const struct nvkm_acr_hsf_func +gp102_acr_load_0 = { + .load = gp102_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gm200_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gp102/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp102/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp104/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp104/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp106/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp106/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp107/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp107/acr/ucode_load.bin"); + +static const struct nvkm_acr_hsf_fwif +gp102_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp102_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gp102_acr = { + .load = gp102_acr_load_fwif, + .unload = gp102_acr_unload_fwif, + .wpr_parse = gp102_acr_wpr_parse, + .wpr_layout = gp102_acr_wpr_layout, + .wpr_alloc = gp102_acr_wpr_alloc, + .wpr_build = gp102_acr_wpr_build, + .wpr_patch = gp102_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +int +gp102_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad", + acr, "acr/bl", "acr/ucode_load", "load"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload", + acr, "acr/unload_bl", "acr/ucode_unload", + "unload"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +gp102_acr_fwif[] = { + { 0, gp102_acr_load, &gp102_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gp102_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gp102_acr_fwif, device, type, inst, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c new file mode 100644 index 000000000..373d638a2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c @@ -0,0 +1,113 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include + +#include + +void +gp108_acr_hsfw_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + struct flcn_bl_dmem_desc_v2 hsdesc = { + .ctx_dma = FALCON_DMAIDX_VIRT, + .code_dma_base = hsf->vma->addr, + .non_sec_code_off = hsf->non_sec_addr, + .non_sec_code_size = hsf->non_sec_size, + .sec_code_off = hsf->sec_addr, + .sec_code_size = hsf->sec_size, + .code_entry_point = 0, + .data_dma_base = hsf->vma->addr + hsf->data_addr, + .data_size = hsf->data_size, + .argc = 0, + .argv = 0, + }; + + flcn_bl_dmem_desc_v2_dump(&acr->subdev, &hsdesc); + + nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0); +} + +const struct nvkm_acr_hsf_func +gp108_acr_unload_0 = { + .load = gm200_acr_unload_load, + .boot = gm200_acr_unload_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gp108/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp108/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gv100/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gv100/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +gp108_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp108_acr_unload_0 }, + {} +}; + +static const struct nvkm_acr_hsf_func +gp108_acr_load_0 = { + .load = gp102_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gp108/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp108/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gv100/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gv100/acr/ucode_load.bin"); + +static const struct nvkm_acr_hsf_fwif +gp108_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp108_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gp108_acr = { + .load = gp108_acr_load_fwif, + .unload = gp108_acr_unload_fwif, + .wpr_parse = gp102_acr_wpr_parse, + .wpr_layout = gp102_acr_wpr_layout, + .wpr_alloc = gp102_acr_wpr_alloc, + .wpr_build = gp102_acr_wpr_build, + .wpr_patch = gp102_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +static const struct nvkm_acr_fwif +gp108_acr_fwif[] = { + { 0, gp102_acr_load, &gp108_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gp108_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gp108_acr_fwif, device, type, inst, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c new file mode 100644 index 000000000..f03ba0288 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) +MODULE_FIRMWARE("nvidia/gp10b/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp10b/acr/ucode_load.bin"); +#endif + +static const struct nvkm_acr_hsf_fwif +gp10b_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm20b_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gp10b_acr = { + .load = gp10b_acr_load_fwif, + .wpr_parse = gm200_acr_wpr_parse, + .wpr_layout = gm200_acr_wpr_layout, + .wpr_alloc = gm20b_acr_wpr_alloc, + .wpr_build = gm200_acr_wpr_build, + .wpr_patch = gm200_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +static const struct nvkm_acr_fwif +gp10b_acr_fwif[] = { + { 0, gm20b_acr_load, &gp10b_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gp10b_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gp10b_acr_fwif, device, type, inst, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c new file mode 100644 index 000000000..a6ea89a5d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c @@ -0,0 +1,177 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include + +#include +#include + +static void +nvkm_acr_hsfw_del(struct nvkm_acr_hsfw *hsfw) +{ + list_del(&hsfw->head); + kfree(hsfw->imem); + kfree(hsfw->image); + kfree(hsfw->sig.prod.data); + kfree(hsfw->sig.dbg.data); + kfree(hsfw); +} + +void +nvkm_acr_hsfw_del_all(struct nvkm_acr *acr) +{ + struct nvkm_acr_hsfw *hsfw, *hsft; + list_for_each_entry_safe(hsfw, hsft, &acr->hsfw, head) { + nvkm_acr_hsfw_del(hsfw); + } +} + +static int +nvkm_acr_hsfw_load_image(struct nvkm_acr *acr, const char *name, int ver, + struct nvkm_acr_hsfw *hsfw) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct firmware *fw; + const struct nvfw_bin_hdr *hdr; + const struct nvfw_hs_header *fwhdr; + const struct nvfw_hs_load_header *lhdr; + u32 loc, sig; + int ret; + + ret = nvkm_firmware_get(subdev, name, ver, &fw); + if (ret < 0) + return ret; + + hdr = nvfw_bin_hdr(subdev, fw->data); + fwhdr = nvfw_hs_header(subdev, fw->data + hdr->header_offset); + + /* Earlier FW releases by NVIDIA for Nouveau's use aren't in NVIDIA's + * standard format, and don't have the indirection seen in the 0x10de + * case. + */ + switch (hdr->bin_magic) { + case 0x000010de: + loc = *(u32 *)(fw->data + fwhdr->patch_loc); + sig = *(u32 *)(fw->data + fwhdr->patch_sig); + break; + case 0x3b1d14f0: + loc = fwhdr->patch_loc; + sig = fwhdr->patch_sig; + break; + default: + ret = -EINVAL; + goto done; + } + + lhdr = nvfw_hs_load_header(subdev, fw->data + fwhdr->hdr_offset); + + if (!(hsfw->image = kmalloc(hdr->data_size, GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + + memcpy(hsfw->image, fw->data + hdr->data_offset, hdr->data_size); + hsfw->image_size = hdr->data_size; + hsfw->non_sec_addr = lhdr->non_sec_code_off; + hsfw->non_sec_size = lhdr->non_sec_code_size; + hsfw->sec_addr = lhdr->apps[0]; + hsfw->sec_size = lhdr->apps[lhdr->num_apps]; + hsfw->data_addr = lhdr->data_dma_base; + hsfw->data_size = lhdr->data_size; + + hsfw->sig.prod.size = fwhdr->sig_prod_size; + hsfw->sig.prod.data = kmemdup(fw->data + fwhdr->sig_prod_offset + sig, + hsfw->sig.prod.size, GFP_KERNEL); + if (!hsfw->sig.prod.data) { + ret = -ENOMEM; + goto done; + } + + hsfw->sig.dbg.size = fwhdr->sig_dbg_size; + hsfw->sig.dbg.data = kmemdup(fw->data + fwhdr->sig_dbg_offset + sig, + hsfw->sig.dbg.size, GFP_KERNEL); + if (!hsfw->sig.dbg.data) { + ret = -ENOMEM; + goto done; + } + + hsfw->sig.patch_loc = loc; +done: + nvkm_firmware_put(fw); + return ret; +} + +static int +nvkm_acr_hsfw_load_bl(struct nvkm_acr *acr, const char *name, int ver, + struct nvkm_acr_hsfw *hsfw) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvfw_bin_hdr *hdr; + const struct nvfw_bl_desc *desc; + const struct firmware *fw; + u8 *data; + int ret; + + ret = nvkm_firmware_get(subdev, name, ver, &fw); + if (ret) + return ret; + + hdr = nvfw_bin_hdr(subdev, fw->data); + desc = nvfw_bl_desc(subdev, fw->data + hdr->header_offset); + data = (void *)fw->data + hdr->data_offset; + + hsfw->imem_size = desc->code_size; + hsfw->imem_tag = desc->start_tag; + hsfw->imem = kmemdup(data + desc->code_off, desc->code_size, GFP_KERNEL); + nvkm_firmware_put(fw); + if (!hsfw->imem) + return -ENOMEM; + else + return 0; +} + +int +nvkm_acr_hsfw_load(struct nvkm_acr *acr, const char *bl, const char *fw, + const char *name, int version, + const struct nvkm_acr_hsf_fwif *fwif) +{ + struct nvkm_acr_hsfw *hsfw; + int ret; + + if (!(hsfw = kzalloc(sizeof(*hsfw), GFP_KERNEL))) + return -ENOMEM; + + hsfw->func = fwif->func; + hsfw->name = name; + list_add_tail(&hsfw->head, &acr->hsfw); + + ret = nvkm_acr_hsfw_load_bl(acr, bl, version, hsfw); + if (ret) + goto done; + + ret = nvkm_acr_hsfw_load_image(acr, fw, version, hsfw); +done: + if (ret) + nvkm_acr_hsfw_del(hsfw); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c new file mode 100644 index 000000000..9b1cf6711 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c @@ -0,0 +1,253 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include +#include +#include +#include + +void +nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *lsfw) +{ + nvkm_blob_dtor(&lsfw->img); + nvkm_firmware_put(lsfw->sig); + list_del(&lsfw->head); + kfree(lsfw); +} + +void +nvkm_acr_lsfw_del_all(struct nvkm_acr *acr) +{ + struct nvkm_acr_lsfw *lsfw, *lsft; + list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { + nvkm_acr_lsfw_del(lsfw); + } +} + +static struct nvkm_acr_lsfw * +nvkm_acr_lsfw_get(struct nvkm_acr *acr, enum nvkm_acr_lsf_id id) +{ + struct nvkm_acr_lsfw *lsfw; + list_for_each_entry(lsfw, &acr->lsfw, head) { + if (lsfw->id == id) + return lsfw; + } + return NULL; +} + +struct nvkm_acr_lsfw * +nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *func, struct nvkm_acr *acr, + struct nvkm_falcon *falcon, enum nvkm_acr_lsf_id id) +{ + struct nvkm_acr_lsfw *lsfw; + + if (!acr || list_empty(&acr->hsfw)) + return ERR_PTR(-ENOSYS); + + lsfw = nvkm_acr_lsfw_get(acr, id); + if (lsfw && lsfw->func) { + nvkm_error(&acr->subdev, "LSFW %d redefined\n", id); + return ERR_PTR(-EEXIST); + } + + if (!lsfw) { + if (!(lsfw = kzalloc(sizeof(*lsfw), GFP_KERNEL))) + return ERR_PTR(-ENOMEM); + + lsfw->id = id; + list_add_tail(&lsfw->head, &acr->lsfw); + } + + lsfw->func = func; + lsfw->falcon = falcon; + return lsfw; +} + +static struct nvkm_acr_lsfw * +nvkm_acr_lsfw_load_sig_image_desc_(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func, + const struct firmware **pdesc) +{ + struct nvkm_acr *acr = subdev->device->acr; + struct nvkm_acr_lsfw *lsfw; + int ret; + + if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id)))) + return lsfw; + + ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig); + if (ret) + goto done; + + ret = nvkm_firmware_load_blob(subdev, path, "image", ver, &lsfw->img); + if (ret) + goto done; + + ret = nvkm_firmware_load_name(subdev, path, "desc", ver, pdesc); +done: + if (ret) { + nvkm_acr_lsfw_del(lsfw); + return ERR_PTR(ret); + } + + return lsfw; +} + +static void +nvkm_acr_lsfw_from_desc(const struct nvfw_ls_desc_head *desc, + struct nvkm_acr_lsfw *lsfw) +{ + lsfw->bootloader_size = ALIGN(desc->bootloader_size, 256); + lsfw->bootloader_imem_offset = desc->bootloader_imem_offset; + + lsfw->app_size = ALIGN(desc->app_size, 256); + lsfw->app_start_offset = desc->app_start_offset; + lsfw->app_imem_entry = desc->app_imem_entry; + lsfw->app_resident_code_offset = desc->app_resident_code_offset; + lsfw->app_resident_code_size = desc->app_resident_code_size; + lsfw->app_resident_data_offset = desc->app_resident_data_offset; + lsfw->app_resident_data_size = desc->app_resident_data_size; + + lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) + + lsfw->bootloader_size; + lsfw->data_size = lsfw->app_size + lsfw->bootloader_size - + lsfw->ucode_size; +} + +int +nvkm_acr_lsfw_load_sig_image_desc(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func) +{ + const struct firmware *fw; + struct nvkm_acr_lsfw *lsfw; + + lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver, + func, &fw); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + + nvkm_acr_lsfw_from_desc(&nvfw_ls_desc(subdev, fw->data)->head, lsfw); + nvkm_firmware_put(fw); + return 0; +} + +int +nvkm_acr_lsfw_load_sig_image_desc_v1(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func) +{ + const struct firmware *fw; + struct nvkm_acr_lsfw *lsfw; + + lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver, + func, &fw); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + + nvkm_acr_lsfw_from_desc(&nvfw_ls_desc_v1(subdev, fw->data)->head, lsfw); + nvkm_firmware_put(fw); + return 0; +} + +int +nvkm_acr_lsfw_load_bl_inst_data_sig(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func) +{ + struct nvkm_acr *acr = subdev->device->acr; + struct nvkm_acr_lsfw *lsfw; + const struct firmware *bl = NULL, *inst = NULL, *data = NULL; + const struct nvfw_bin_hdr *hdr; + const struct nvfw_bl_desc *desc; + u32 *bldata; + int ret; + + if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id)))) + return PTR_ERR(lsfw); + + ret = nvkm_firmware_load_name(subdev, path, "bl", ver, &bl); + if (ret) + goto done; + + hdr = nvfw_bin_hdr(subdev, bl->data); + desc = nvfw_bl_desc(subdev, bl->data + hdr->header_offset); + bldata = (void *)(bl->data + hdr->data_offset); + + ret = nvkm_firmware_load_name(subdev, path, "inst", ver, &inst); + if (ret) + goto done; + + ret = nvkm_firmware_load_name(subdev, path, "data", ver, &data); + if (ret) + goto done; + + ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig); + if (ret) + goto done; + + lsfw->bootloader_size = ALIGN(desc->code_size, 256); + lsfw->bootloader_imem_offset = desc->start_tag << 8; + + lsfw->app_start_offset = lsfw->bootloader_size; + lsfw->app_imem_entry = 0; + lsfw->app_resident_code_offset = 0; + lsfw->app_resident_code_size = ALIGN(inst->size, 256); + lsfw->app_resident_data_offset = lsfw->app_resident_code_size; + lsfw->app_resident_data_size = ALIGN(data->size, 256); + lsfw->app_size = lsfw->app_resident_code_size + + lsfw->app_resident_data_size; + + lsfw->img.size = lsfw->bootloader_size + lsfw->app_size; + if (!(lsfw->img.data = kzalloc(lsfw->img.size, GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + + memcpy(lsfw->img.data, bldata, lsfw->bootloader_size); + memcpy(lsfw->img.data + lsfw->app_start_offset + + lsfw->app_resident_code_offset, inst->data, inst->size); + memcpy(lsfw->img.data + lsfw->app_start_offset + + lsfw->app_resident_data_offset, data->data, data->size); + + lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) + + lsfw->bootloader_size; + lsfw->data_size = lsfw->app_size + lsfw->bootloader_size - + lsfw->ucode_size; + +done: + if (ret) + nvkm_acr_lsfw_del(lsfw); + nvkm_firmware_put(data); + nvkm_firmware_put(inst); + nvkm_firmware_put(bl); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h new file mode 100644 index 000000000..c30b841c9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h @@ -0,0 +1,154 @@ +#ifndef __NVKM_ACR_PRIV_H__ +#define __NVKM_ACR_PRIV_H__ +#include +struct lsb_header_tail; + +struct nvkm_acr_fwif { + int version; + int (*load)(struct nvkm_acr *, int version, + const struct nvkm_acr_fwif *); + const struct nvkm_acr_func *func; +}; + +int gm200_acr_nofw(struct nvkm_acr *, int, const struct nvkm_acr_fwif *); +int gm20b_acr_load(struct nvkm_acr *, int, const struct nvkm_acr_fwif *); +int gp102_acr_load(struct nvkm_acr *, int, const struct nvkm_acr_fwif *); + +struct nvkm_acr_lsf; +struct nvkm_acr_func { + const struct nvkm_acr_hsf_fwif *load; + const struct nvkm_acr_hsf_fwif *ahesasc; + const struct nvkm_acr_hsf_fwif *asb; + const struct nvkm_acr_hsf_fwif *unload; + int (*wpr_parse)(struct nvkm_acr *); + u32 (*wpr_layout)(struct nvkm_acr *); + int (*wpr_alloc)(struct nvkm_acr *, u32 wpr_size); + int (*wpr_build)(struct nvkm_acr *, struct nvkm_acr_lsf *rtos); + void (*wpr_patch)(struct nvkm_acr *, s64 adjust); + void (*wpr_check)(struct nvkm_acr *, u64 *start, u64 *limit); + int (*init)(struct nvkm_acr *); + void (*fini)(struct nvkm_acr *); + u64 bootstrap_falcons; +}; + +extern const struct nvkm_acr_func gm200_acr; +int gm200_acr_wpr_parse(struct nvkm_acr *); +u32 gm200_acr_wpr_layout(struct nvkm_acr *); +int gm200_acr_wpr_build(struct nvkm_acr *, struct nvkm_acr_lsf *); +void gm200_acr_wpr_patch(struct nvkm_acr *, s64); +void gm200_acr_wpr_check(struct nvkm_acr *, u64 *, u64 *); +void gm200_acr_wpr_build_lsb_tail(struct nvkm_acr_lsfw *, + struct lsb_header_tail *); +int gm200_acr_init(struct nvkm_acr *); + +int gm20b_acr_wpr_alloc(struct nvkm_acr *, u32 wpr_size); + +int gp102_acr_wpr_parse(struct nvkm_acr *); +u32 gp102_acr_wpr_layout(struct nvkm_acr *); +int gp102_acr_wpr_alloc(struct nvkm_acr *, u32 wpr_size); +int gp102_acr_wpr_build(struct nvkm_acr *, struct nvkm_acr_lsf *); +int gp102_acr_wpr_build_lsb(struct nvkm_acr *, struct nvkm_acr_lsfw *); +void gp102_acr_wpr_patch(struct nvkm_acr *, s64); + +struct nvkm_acr_hsfw { + const struct nvkm_acr_hsf_func *func; + const char *name; + struct list_head head; + + u32 imem_size; + u32 imem_tag; + u32 *imem; + + u8 *image; + u32 image_size; + u32 non_sec_addr; + u32 non_sec_size; + u32 sec_addr; + u32 sec_size; + u32 data_addr; + u32 data_size; + + struct { + struct { + void *data; + u32 size; + } prod, dbg; + u32 patch_loc; + } sig; +}; + +struct nvkm_acr_hsf_fwif { + int version; + int (*load)(struct nvkm_acr *, const char *bl, const char *fw, + const char *name, int version, + const struct nvkm_acr_hsf_fwif *); + const struct nvkm_acr_hsf_func *func; +}; + +int nvkm_acr_hsfw_load(struct nvkm_acr *, const char *, const char *, + const char *, int, const struct nvkm_acr_hsf_fwif *); +void nvkm_acr_hsfw_del_all(struct nvkm_acr *); + +struct nvkm_acr_hsf { + const struct nvkm_acr_hsf_func *func; + const char *name; + struct list_head head; + + u32 imem_size; + u32 imem_tag; + u32 *imem; + + u32 non_sec_addr; + u32 non_sec_size; + u32 sec_addr; + u32 sec_size; + u32 data_addr; + u32 data_size; + + struct nvkm_memory *ucode; + struct nvkm_vma *vma; + struct nvkm_falcon *falcon; +}; + +struct nvkm_acr_hsf_func { + int (*load)(struct nvkm_acr *, struct nvkm_acr_hsfw *); + int (*boot)(struct nvkm_acr *, struct nvkm_acr_hsf *); + void (*bld)(struct nvkm_acr *, struct nvkm_acr_hsf *); +}; + +int gm200_acr_hsfw_load(struct nvkm_acr *, struct nvkm_acr_hsfw *, + struct nvkm_falcon *); +int gm200_acr_hsfw_boot(struct nvkm_acr *, struct nvkm_acr_hsf *, + u32 clear_intr, u32 mbox0_ok); + +int gm200_acr_load_boot(struct nvkm_acr *, struct nvkm_acr_hsf *); + +extern const struct nvkm_acr_hsf_func gm200_acr_unload_0; +int gm200_acr_unload_load(struct nvkm_acr *, struct nvkm_acr_hsfw *); +int gm200_acr_unload_boot(struct nvkm_acr *, struct nvkm_acr_hsf *); +void gm200_acr_hsfw_bld(struct nvkm_acr *, struct nvkm_acr_hsf *); + +extern const struct nvkm_acr_hsf_func gm20b_acr_load_0; + +int gp102_acr_load_load(struct nvkm_acr *, struct nvkm_acr_hsfw *); + +extern const struct nvkm_acr_hsf_func gp108_acr_unload_0; +void gp108_acr_hsfw_bld(struct nvkm_acr *, struct nvkm_acr_hsf *); + +int nvkm_acr_new_(const struct nvkm_acr_fwif *, struct nvkm_device *, enum nvkm_subdev_type, + int inst, struct nvkm_acr **); +int nvkm_acr_hsf_boot(struct nvkm_acr *, const char *name); + +struct nvkm_acr_lsf { + const struct nvkm_acr_lsf_func *func; + struct nvkm_falcon *falcon; + enum nvkm_acr_lsf_id id; + struct list_head head; +}; + +struct nvkm_acr_lsfw *nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *, + struct nvkm_acr *, struct nvkm_falcon *, + enum nvkm_acr_lsf_id); +void nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *); +void nvkm_acr_lsfw_del_all(struct nvkm_acr *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c new file mode 100644 index 000000000..05a87e775 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c @@ -0,0 +1,231 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include +#include +#include + +#include + +static int +tu102_acr_init(struct nvkm_acr *acr) +{ + int ret = nvkm_acr_hsf_boot(acr, "AHESASC"); + if (ret) + return ret; + + return nvkm_acr_hsf_boot(acr, "ASB"); +} + +static int +tu102_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos) +{ + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + int ret; + + /*XXX: shared sub-WPR headers, fill terminator for now. */ + nvkm_wo32(acr->wpr, 0x200, 0xffffffff); + + /* Fill per-LSF structures. */ + list_for_each_entry(lsfw, &acr->lsfw, head) { + struct lsf_signature_v1 *sig = (void *)lsfw->sig->data; + struct wpr_header_v1 hdr = { + .falcon_id = lsfw->id, + .lsb_offset = lsfw->offset.lsb, + .bootstrap_owner = NVKM_ACR_LSF_GSPLITE, + .lazy_bootstrap = 1, + .bin_version = sig->version, + .status = WPR_HEADER_V1_STATUS_COPY, + }; + + /* Write WPR header. */ + nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr)); + offset += sizeof(hdr); + + /* Write LSB header. */ + ret = gp102_acr_wpr_build_lsb(acr, lsfw); + if (ret) + return ret; + + /* Write ucode image. */ + nvkm_wobj(acr->wpr, lsfw->offset.img, + lsfw->img.data, + lsfw->img.size); + + /* Write bootloader data. */ + lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw); + } + + /* Finalise WPR. */ + nvkm_wo32(acr->wpr, offset, WPR_HEADER_V1_FALCON_ID_INVALID); + return 0; +} + +static int +tu102_acr_hsfw_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + return gm200_acr_hsfw_boot(acr, hsf, 0, 0); +} + +static int +tu102_acr_hsfw_nofw(struct nvkm_acr *acr, const char *bl, const char *fw, + const char *name, int version, + const struct nvkm_acr_hsf_fwif *fwif) +{ + return 0; +} + +MODULE_FIRMWARE("nvidia/tu102/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu102/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu104/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu104/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu106/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu106/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu116/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu116/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu117/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu117/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +tu102_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp108_acr_unload_0 }, + { -1, tu102_acr_hsfw_nofw }, + {} +}; + +static int +tu102_acr_asb_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->gsp->falcon); +} + +static const struct nvkm_acr_hsf_func +tu102_acr_asb_0 = { + .load = tu102_acr_asb_load, + .boot = tu102_acr_hsfw_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/tu102/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu104/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu106/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu116/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu117/acr/ucode_asb.bin"); + +static const struct nvkm_acr_hsf_fwif +tu102_acr_asb_fwif[] = { + { 0, nvkm_acr_hsfw_load, &tu102_acr_asb_0 }, + { -1, tu102_acr_hsfw_nofw }, + {} +}; + +static const struct nvkm_acr_hsf_func +tu102_acr_ahesasc_0 = { + .load = gp102_acr_load_load, + .boot = tu102_acr_hsfw_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/tu102/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu102/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu104/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu104/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu106/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu106/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu116/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu116/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu117/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu117/acr/ucode_ahesasc.bin"); + +static const struct nvkm_acr_hsf_fwif +tu102_acr_ahesasc_fwif[] = { + { 0, nvkm_acr_hsfw_load, &tu102_acr_ahesasc_0 }, + { -1, tu102_acr_hsfw_nofw }, + {} +}; + +static const struct nvkm_acr_func +tu102_acr = { + .ahesasc = tu102_acr_ahesasc_fwif, + .asb = tu102_acr_asb_fwif, + .unload = tu102_acr_unload_fwif, + .wpr_parse = gp102_acr_wpr_parse, + .wpr_layout = gp102_acr_wpr_layout, + .wpr_alloc = gp102_acr_wpr_alloc, + .wpr_patch = gp102_acr_wpr_patch, + .wpr_build = tu102_acr_wpr_build, + .wpr_check = gm200_acr_wpr_check, + .init = tu102_acr_init, +}; + +static int +tu102_acr_load(struct nvkm_acr *acr, int version, + const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->ahesasc, "AcrAHESASC", + acr, "acr/bl", "acr/ucode_ahesasc", + "AHESASC"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->asb, "AcrASB", + acr, "acr/bl", "acr/ucode_asb", "ASB"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload", + acr, "acr/unload_bl", "acr/ucode_unload", + "unload"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +tu102_acr_fwif[] = { + { 0, tu102_acr_load, &tu102_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +tu102_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(tu102_acr_fwif, device, type, inst, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild new file mode 100644 index 000000000..8faee3317 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/bar/base.o +nvkm-y += nvkm/subdev/bar/nv50.o +nvkm-y += nvkm/subdev/bar/g84.o +nvkm-y += nvkm/subdev/bar/gf100.o +nvkm-y += nvkm/subdev/bar/gk20a.o +nvkm-y += nvkm/subdev/bar/gm107.o +nvkm-y += nvkm/subdev/bar/gm20b.o +nvkm-y += nvkm/subdev/bar/tu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c new file mode 100644 index 000000000..d017a1b5e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c @@ -0,0 +1,142 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +void +nvkm_bar_flush(struct nvkm_bar *bar) +{ + if (bar && bar->func->flush) + bar->func->flush(bar); +} + +struct nvkm_vmm * +nvkm_bar_bar1_vmm(struct nvkm_device *device) +{ + return device->bar->func->bar1.vmm(device->bar); +} + +void +nvkm_bar_bar1_reset(struct nvkm_device *device) +{ + struct nvkm_bar *bar = device->bar; + if (bar) { + bar->func->bar1.init(bar); + bar->func->bar1.wait(bar); + } +} + +struct nvkm_vmm * +nvkm_bar_bar2_vmm(struct nvkm_device *device) +{ + /* Denies access to BAR2 when it's not initialised, used by INSTMEM + * to know when object access needs to go through the BAR0 window. + */ + struct nvkm_bar *bar = device->bar; + if (bar && bar->bar2) + return bar->func->bar2.vmm(bar); + return NULL; +} + +void +nvkm_bar_bar2_reset(struct nvkm_device *device) +{ + struct nvkm_bar *bar = device->bar; + if (bar && bar->bar2) { + bar->func->bar2.init(bar); + bar->func->bar2.wait(bar); + } +} + +void +nvkm_bar_bar2_fini(struct nvkm_device *device) +{ + struct nvkm_bar *bar = device->bar; + if (bar && bar->bar2) { + bar->func->bar2.fini(bar); + bar->bar2 = false; + } +} + +void +nvkm_bar_bar2_init(struct nvkm_device *device) +{ + struct nvkm_bar *bar = device->bar; + if (bar && bar->subdev.oneinit && !bar->bar2 && bar->func->bar2.init) { + bar->func->bar2.init(bar); + bar->func->bar2.wait(bar); + bar->bar2 = true; + } +} + +static int +nvkm_bar_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_bar *bar = nvkm_bar(subdev); + if (bar->func->bar1.fini) + bar->func->bar1.fini(bar); + return 0; +} + +static int +nvkm_bar_init(struct nvkm_subdev *subdev) +{ + struct nvkm_bar *bar = nvkm_bar(subdev); + bar->func->bar1.init(bar); + bar->func->bar1.wait(bar); + if (bar->func->init) + bar->func->init(bar); + return 0; +} + +static int +nvkm_bar_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_bar *bar = nvkm_bar(subdev); + return bar->func->oneinit(bar); +} + +static void * +nvkm_bar_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_bar *bar = nvkm_bar(subdev); + nvkm_bar_bar2_fini(subdev->device); + return bar->func->dtor(bar); +} + +static const struct nvkm_subdev_func +nvkm_bar = { + .dtor = nvkm_bar_dtor, + .oneinit = nvkm_bar_oneinit, + .init = nvkm_bar_init, + .fini = nvkm_bar_fini, +}; + +void +nvkm_bar_ctor(const struct nvkm_bar_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_bar *bar) +{ + nvkm_subdev_ctor(&nvkm_bar, device, type, inst, &bar->subdev); + bar->func = func; + spin_lock_init(&bar->lock); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/g84.c new file mode 100644 index 000000000..77a41bcf8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/g84.c @@ -0,0 +1,63 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include + +void +g84_bar_flush(struct nvkm_bar *bar) +{ + struct nvkm_device *device = bar->subdev.device; + unsigned long flags; + spin_lock_irqsave(&bar->lock, flags); + nvkm_wr32(device, 0x070000, 0x00000001); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x070000) & 0x00000002)) + break; + ); + spin_unlock_irqrestore(&bar->lock, flags); +} + +static const struct nvkm_bar_func +g84_bar_func = { + .dtor = nv50_bar_dtor, + .oneinit = nv50_bar_oneinit, + .init = nv50_bar_init, + .bar1.init = nv50_bar_bar1_init, + .bar1.fini = nv50_bar_bar1_fini, + .bar1.wait = nv50_bar_bar1_wait, + .bar1.vmm = nv50_bar_bar1_vmm, + .bar2.init = nv50_bar_bar2_init, + .bar2.fini = nv50_bar_bar2_fini, + .bar2.wait = nv50_bar_bar1_wait, + .bar2.vmm = nv50_bar_bar2_vmm, + .flush = g84_bar_flush, +}; + +int +g84_bar_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bar **pbar) +{ + return nv50_bar_new_(&g84_bar_func, device, type, inst, 0x200, pbar); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c new file mode 100644 index 000000000..51070b7dd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c @@ -0,0 +1,196 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" + +#include +#include +#include +#include + +struct nvkm_vmm * +gf100_bar_bar1_vmm(struct nvkm_bar *base) +{ + return gf100_bar(base)->bar[1].vmm; +} + +void +gf100_bar_bar1_wait(struct nvkm_bar *base) +{ + /* NFI why it's twice. */ + nvkm_bar_flush(base); + nvkm_bar_flush(base); +} + +void +gf100_bar_bar1_fini(struct nvkm_bar *bar) +{ + nvkm_mask(bar->subdev.device, 0x001704, 0x80000000, 0x00000000); +} + +void +gf100_bar_bar1_init(struct nvkm_bar *base) +{ + struct nvkm_device *device = base->subdev.device; + struct gf100_bar *bar = gf100_bar(base); + const u32 addr = nvkm_memory_addr(bar->bar[1].inst) >> 12; + nvkm_wr32(device, 0x001704, 0x80000000 | addr); +} + +struct nvkm_vmm * +gf100_bar_bar2_vmm(struct nvkm_bar *base) +{ + return gf100_bar(base)->bar[0].vmm; +} + +void +gf100_bar_bar2_fini(struct nvkm_bar *bar) +{ + nvkm_mask(bar->subdev.device, 0x001714, 0x80000000, 0x00000000); +} + +void +gf100_bar_bar2_init(struct nvkm_bar *base) +{ + struct nvkm_device *device = base->subdev.device; + struct gf100_bar *bar = gf100_bar(base); + u32 addr = nvkm_memory_addr(bar->bar[0].inst) >> 12; + if (bar->bar2_halve) + addr |= 0x40000000; + nvkm_wr32(device, 0x001714, 0x80000000 | addr); +} + +static int +gf100_bar_oneinit_bar(struct gf100_bar *bar, struct gf100_barN *bar_vm, + struct lock_class_key *key, int bar_nr) +{ + struct nvkm_device *device = bar->base.subdev.device; + resource_size_t bar_len; + int ret; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0, false, + &bar_vm->inst); + if (ret) + return ret; + + bar_len = device->func->resource_size(device, bar_nr); + if (!bar_len) + return -ENOMEM; + if (bar_nr == 3 && bar->bar2_halve) + bar_len >>= 1; + + ret = nvkm_vmm_new(device, 0, bar_len, NULL, 0, key, + (bar_nr == 3) ? "bar2" : "bar1", &bar_vm->vmm); + if (ret) + return ret; + + atomic_inc(&bar_vm->vmm->engref[NVKM_SUBDEV_BAR]); + bar_vm->vmm->debug = bar->base.subdev.debug; + + /* + * Bootstrap page table lookup. + */ + if (bar_nr == 3) { + ret = nvkm_vmm_boot(bar_vm->vmm); + if (ret) + return ret; + } + + return nvkm_vmm_join(bar_vm->vmm, bar_vm->inst); +} + +int +gf100_bar_oneinit(struct nvkm_bar *base) +{ + static struct lock_class_key bar1_lock; + static struct lock_class_key bar2_lock; + struct gf100_bar *bar = gf100_bar(base); + int ret; + + /* BAR2 */ + if (bar->base.func->bar2.init) { + ret = gf100_bar_oneinit_bar(bar, &bar->bar[0], &bar2_lock, 3); + if (ret) + return ret; + + bar->base.subdev.oneinit = true; + nvkm_bar_bar2_init(bar->base.subdev.device); + } + + /* BAR1 */ + ret = gf100_bar_oneinit_bar(bar, &bar->bar[1], &bar1_lock, 1); + if (ret) + return ret; + + return 0; +} + +void * +gf100_bar_dtor(struct nvkm_bar *base) +{ + struct gf100_bar *bar = gf100_bar(base); + + nvkm_vmm_part(bar->bar[1].vmm, bar->bar[1].inst); + nvkm_vmm_unref(&bar->bar[1].vmm); + nvkm_memory_unref(&bar->bar[1].inst); + + nvkm_vmm_part(bar->bar[0].vmm, bar->bar[0].inst); + nvkm_vmm_unref(&bar->bar[0].vmm); + nvkm_memory_unref(&bar->bar[0].inst); + return bar; +} + +int +gf100_bar_new_(const struct nvkm_bar_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_bar **pbar) +{ + struct gf100_bar *bar; + if (!(bar = kzalloc(sizeof(*bar), GFP_KERNEL))) + return -ENOMEM; + nvkm_bar_ctor(func, device, type, inst, &bar->base); + bar->bar2_halve = nvkm_boolopt(device->cfgopt, "NvBar2Halve", false); + *pbar = &bar->base; + return 0; +} + +static const struct nvkm_bar_func +gf100_bar_func = { + .dtor = gf100_bar_dtor, + .oneinit = gf100_bar_oneinit, + .bar1.init = gf100_bar_bar1_init, + .bar1.fini = gf100_bar_bar1_fini, + .bar1.wait = gf100_bar_bar1_wait, + .bar1.vmm = gf100_bar_bar1_vmm, + .bar2.init = gf100_bar_bar2_init, + .bar2.fini = gf100_bar_bar2_fini, + .bar2.wait = gf100_bar_bar1_wait, + .bar2.vmm = gf100_bar_bar2_vmm, + .flush = g84_bar_flush, +}; + +int +gf100_bar_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bar **pbar) +{ + return gf100_bar_new_(&gf100_bar_func, device, type, inst, pbar); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.h new file mode 100644 index 000000000..328a68b41 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __GF100_BAR_H__ +#define __GF100_BAR_H__ +#define gf100_bar(p) container_of((p), struct gf100_bar, base) +#include "priv.h" + +struct gf100_barN { + struct nvkm_memory *inst; + struct nvkm_vmm *vmm; +}; + +struct gf100_bar { + struct nvkm_bar base; + bool bar2_halve; + struct gf100_barN bar[2]; +}; + +int gf100_bar_new_(const struct nvkm_bar_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_bar **); +void *gf100_bar_dtor(struct nvkm_bar *); +int gf100_bar_oneinit(struct nvkm_bar *); +void gf100_bar_bar1_init(struct nvkm_bar *); +void gf100_bar_bar1_wait(struct nvkm_bar *); +struct nvkm_vmm *gf100_bar_bar1_vmm(struct nvkm_bar *); +void gf100_bar_bar2_init(struct nvkm_bar *); +struct nvkm_vmm *gf100_bar_bar2_vmm(struct nvkm_bar *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c new file mode 100644 index 000000000..eead8ab88 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" + +static const struct nvkm_bar_func +gk20a_bar_func = { + .dtor = gf100_bar_dtor, + .oneinit = gf100_bar_oneinit, + .bar1.init = gf100_bar_bar1_init, + .bar1.wait = gf100_bar_bar1_wait, + .bar1.vmm = gf100_bar_bar1_vmm, + .flush = g84_bar_flush, +}; + +int +gk20a_bar_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bar **pbar) +{ + int ret = gf100_bar_new_(&gk20a_bar_func, device, type, inst, pbar); + if (ret == 0) + (*pbar)->iomap_uncached = true; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm107.c new file mode 100644 index 000000000..da95307a7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm107.c @@ -0,0 +1,66 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" + +#include + +void +gm107_bar_bar1_wait(struct nvkm_bar *bar) +{ + struct nvkm_device *device = bar->subdev.device; + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x001710) & 0x00000003)) + break; + ); +} + +static void +gm107_bar_bar2_wait(struct nvkm_bar *bar) +{ + struct nvkm_device *device = bar->subdev.device; + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x001710) & 0x0000000c)) + break; + ); +} + +static const struct nvkm_bar_func +gm107_bar_func = { + .dtor = gf100_bar_dtor, + .oneinit = gf100_bar_oneinit, + .bar1.init = gf100_bar_bar1_init, + .bar1.fini = gf100_bar_bar1_fini, + .bar1.wait = gm107_bar_bar1_wait, + .bar1.vmm = gf100_bar_bar1_vmm, + .bar2.init = gf100_bar_bar2_init, + .bar2.fini = gf100_bar_bar2_fini, + .bar2.wait = gm107_bar_bar2_wait, + .bar2.vmm = gf100_bar_bar2_vmm, + .flush = g84_bar_flush, +}; + +int +gm107_bar_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bar **pbar) +{ + return gf100_bar_new_(&gm107_bar_func, device, type, inst, pbar); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm20b.c new file mode 100644 index 000000000..4acdb4fb0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gm20b.c @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" + +static const struct nvkm_bar_func +gm20b_bar_func = { + .dtor = gf100_bar_dtor, + .oneinit = gf100_bar_oneinit, + .bar1.init = gf100_bar_bar1_init, + .bar1.wait = gm107_bar_bar1_wait, + .bar1.vmm = gf100_bar_bar1_vmm, + .flush = g84_bar_flush, +}; + +int +gm20b_bar_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bar **pbar) +{ + int ret = gf100_bar_new_(&gm20b_bar_func, device, type, inst, pbar); + if (ret == 0) + (*pbar)->iomap_uncached = true; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c new file mode 100644 index 000000000..27d8a1be4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c @@ -0,0 +1,255 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include + +static void +nv50_bar_flush(struct nvkm_bar *base) +{ + struct nv50_bar *bar = nv50_bar(base); + struct nvkm_device *device = bar->base.subdev.device; + unsigned long flags; + spin_lock_irqsave(&bar->base.lock, flags); + nvkm_wr32(device, 0x00330c, 0x00000001); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x00330c) & 0x00000002)) + break; + ); + spin_unlock_irqrestore(&bar->base.lock, flags); +} + +struct nvkm_vmm * +nv50_bar_bar1_vmm(struct nvkm_bar *base) +{ + return nv50_bar(base)->bar1_vmm; +} + +void +nv50_bar_bar1_wait(struct nvkm_bar *base) +{ + nvkm_bar_flush(base); +} + +void +nv50_bar_bar1_fini(struct nvkm_bar *bar) +{ + nvkm_wr32(bar->subdev.device, 0x001708, 0x00000000); +} + +void +nv50_bar_bar1_init(struct nvkm_bar *base) +{ + struct nvkm_device *device = base->subdev.device; + struct nv50_bar *bar = nv50_bar(base); + nvkm_wr32(device, 0x001708, 0x80000000 | bar->bar1->node->offset >> 4); +} + +struct nvkm_vmm * +nv50_bar_bar2_vmm(struct nvkm_bar *base) +{ + return nv50_bar(base)->bar2_vmm; +} + +void +nv50_bar_bar2_fini(struct nvkm_bar *bar) +{ + nvkm_wr32(bar->subdev.device, 0x00170c, 0x00000000); +} + +void +nv50_bar_bar2_init(struct nvkm_bar *base) +{ + struct nvkm_device *device = base->subdev.device; + struct nv50_bar *bar = nv50_bar(base); + nvkm_wr32(device, 0x001704, 0x00000000 | bar->mem->addr >> 12); + nvkm_wr32(device, 0x001704, 0x40000000 | bar->mem->addr >> 12); + nvkm_wr32(device, 0x00170c, 0x80000000 | bar->bar2->node->offset >> 4); +} + +void +nv50_bar_init(struct nvkm_bar *base) +{ + struct nv50_bar *bar = nv50_bar(base); + struct nvkm_device *device = bar->base.subdev.device; + int i; + + for (i = 0; i < 8; i++) + nvkm_wr32(device, 0x001900 + (i * 4), 0x00000000); +} + +int +nv50_bar_oneinit(struct nvkm_bar *base) +{ + struct nv50_bar *bar = nv50_bar(base); + struct nvkm_device *device = bar->base.subdev.device; + static struct lock_class_key bar1_lock; + static struct lock_class_key bar2_lock; + u64 start, limit, size; + int ret; + + ret = nvkm_gpuobj_new(device, 0x20000, 0, false, NULL, &bar->mem); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, bar->pgd_addr, 0, false, bar->mem, + &bar->pad); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 0x4000, 0, false, bar->mem, &bar->pgd); + if (ret) + return ret; + + /* BAR2 */ + start = 0x0100000000ULL; + size = device->func->resource_size(device, 3); + if (!size) + return -ENOMEM; + limit = start + size; + + ret = nvkm_vmm_new(device, start, limit-- - start, NULL, 0, + &bar2_lock, "bar2", &bar->bar2_vmm); + if (ret) + return ret; + + atomic_inc(&bar->bar2_vmm->engref[NVKM_SUBDEV_BAR]); + bar->bar2_vmm->debug = bar->base.subdev.debug; + + ret = nvkm_vmm_boot(bar->bar2_vmm); + if (ret) + return ret; + + ret = nvkm_vmm_join(bar->bar2_vmm, bar->mem->memory); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 24, 16, false, bar->mem, &bar->bar2); + if (ret) + return ret; + + nvkm_kmap(bar->bar2); + nvkm_wo32(bar->bar2, 0x00, 0x7fc00000); + nvkm_wo32(bar->bar2, 0x04, lower_32_bits(limit)); + nvkm_wo32(bar->bar2, 0x08, lower_32_bits(start)); + nvkm_wo32(bar->bar2, 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nvkm_wo32(bar->bar2, 0x10, 0x00000000); + nvkm_wo32(bar->bar2, 0x14, 0x00000000); + nvkm_done(bar->bar2); + + bar->base.subdev.oneinit = true; + nvkm_bar_bar2_init(device); + + /* BAR1 */ + start = 0x0000000000ULL; + size = device->func->resource_size(device, 1); + if (!size) + return -ENOMEM; + limit = start + size; + + ret = nvkm_vmm_new(device, start, limit-- - start, NULL, 0, + &bar1_lock, "bar1", &bar->bar1_vmm); + if (ret) + return ret; + + atomic_inc(&bar->bar1_vmm->engref[NVKM_SUBDEV_BAR]); + bar->bar1_vmm->debug = bar->base.subdev.debug; + + ret = nvkm_vmm_join(bar->bar1_vmm, bar->mem->memory); + if (ret) + return ret; + + ret = nvkm_gpuobj_new(device, 24, 16, false, bar->mem, &bar->bar1); + if (ret) + return ret; + + nvkm_kmap(bar->bar1); + nvkm_wo32(bar->bar1, 0x00, 0x7fc00000); + nvkm_wo32(bar->bar1, 0x04, lower_32_bits(limit)); + nvkm_wo32(bar->bar1, 0x08, lower_32_bits(start)); + nvkm_wo32(bar->bar1, 0x0c, upper_32_bits(limit) << 24 | + upper_32_bits(start)); + nvkm_wo32(bar->bar1, 0x10, 0x00000000); + nvkm_wo32(bar->bar1, 0x14, 0x00000000); + nvkm_done(bar->bar1); + return 0; +} + +void * +nv50_bar_dtor(struct nvkm_bar *base) +{ + struct nv50_bar *bar = nv50_bar(base); + if (bar->mem) { + nvkm_gpuobj_del(&bar->bar1); + nvkm_vmm_part(bar->bar1_vmm, bar->mem->memory); + nvkm_vmm_unref(&bar->bar1_vmm); + nvkm_gpuobj_del(&bar->bar2); + nvkm_vmm_part(bar->bar2_vmm, bar->mem->memory); + nvkm_vmm_unref(&bar->bar2_vmm); + nvkm_gpuobj_del(&bar->pgd); + nvkm_gpuobj_del(&bar->pad); + nvkm_gpuobj_del(&bar->mem); + } + return bar; +} + +int +nv50_bar_new_(const struct nvkm_bar_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, u32 pgd_addr, struct nvkm_bar **pbar) +{ + struct nv50_bar *bar; + if (!(bar = kzalloc(sizeof(*bar), GFP_KERNEL))) + return -ENOMEM; + nvkm_bar_ctor(func, device, type, inst, &bar->base); + bar->pgd_addr = pgd_addr; + *pbar = &bar->base; + return 0; +} + +static const struct nvkm_bar_func +nv50_bar_func = { + .dtor = nv50_bar_dtor, + .oneinit = nv50_bar_oneinit, + .init = nv50_bar_init, + .bar1.init = nv50_bar_bar1_init, + .bar1.fini = nv50_bar_bar1_fini, + .bar1.wait = nv50_bar_bar1_wait, + .bar1.vmm = nv50_bar_bar1_vmm, + .bar2.init = nv50_bar_bar2_init, + .bar2.fini = nv50_bar_bar2_fini, + .bar2.wait = nv50_bar_bar1_wait, + .bar2.vmm = nv50_bar_bar2_vmm, + .flush = nv50_bar_flush, +}; + +int +nv50_bar_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bar **pbar) +{ + return nv50_bar_new_(&nv50_bar_func, device, type, inst, 0x1400, pbar); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.h new file mode 100644 index 000000000..dedee9394 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_BAR_H__ +#define __NV50_BAR_H__ +#define nv50_bar(p) container_of((p), struct nv50_bar, base) +#include "priv.h" + +struct nv50_bar { + struct nvkm_bar base; + u32 pgd_addr; + struct nvkm_gpuobj *mem; + struct nvkm_gpuobj *pad; + struct nvkm_gpuobj *pgd; + struct nvkm_vmm *bar1_vmm; + struct nvkm_gpuobj *bar1; + struct nvkm_vmm *bar2_vmm; + struct nvkm_gpuobj *bar2; +}; + +int nv50_bar_new_(const struct nvkm_bar_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, u32 pgd_addr, struct nvkm_bar **); +void *nv50_bar_dtor(struct nvkm_bar *); +int nv50_bar_oneinit(struct nvkm_bar *); +void nv50_bar_init(struct nvkm_bar *); +void nv50_bar_bar1_init(struct nvkm_bar *); +void nv50_bar_bar1_wait(struct nvkm_bar *); +struct nvkm_vmm *nv50_bar_bar1_vmm(struct nvkm_bar *); +void nv50_bar_bar2_init(struct nvkm_bar *); +struct nvkm_vmm *nv50_bar_bar2_vmm(struct nvkm_bar *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h new file mode 100644 index 000000000..daebfc991 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_BAR_PRIV_H__ +#define __NVKM_BAR_PRIV_H__ +#define nvkm_bar(p) container_of((p), struct nvkm_bar, subdev) +#include + +void nvkm_bar_ctor(const struct nvkm_bar_func *, struct nvkm_device *, + enum nvkm_subdev_type, int, struct nvkm_bar *); + +struct nvkm_bar_func { + void *(*dtor)(struct nvkm_bar *); + int (*oneinit)(struct nvkm_bar *); + void (*init)(struct nvkm_bar *); + + struct { + void (*init)(struct nvkm_bar *); + void (*fini)(struct nvkm_bar *); + void (*wait)(struct nvkm_bar *); + struct nvkm_vmm *(*vmm)(struct nvkm_bar *); + } bar1, bar2; + + void (*flush)(struct nvkm_bar *); +}; + +void nv50_bar_bar1_fini(struct nvkm_bar *); +void nv50_bar_bar2_fini(struct nvkm_bar *); + +void g84_bar_flush(struct nvkm_bar *); + +void gf100_bar_bar1_fini(struct nvkm_bar *); +void gf100_bar_bar2_fini(struct nvkm_bar *); + +void gm107_bar_bar1_wait(struct nvkm_bar *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/tu102.c new file mode 100644 index 000000000..c25ab407b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/tu102.c @@ -0,0 +1,99 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" + +#include +#include + +static void +tu102_bar_bar2_wait(struct nvkm_bar *bar) +{ + struct nvkm_device *device = bar->subdev.device; + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0xb80f50) & 0x0000000c)) + break; + ); +} + +static void +tu102_bar_bar2_fini(struct nvkm_bar *bar) +{ + nvkm_mask(bar->subdev.device, 0xb80f48, 0x80000000, 0x00000000); +} + +static void +tu102_bar_bar2_init(struct nvkm_bar *base) +{ + struct nvkm_device *device = base->subdev.device; + struct gf100_bar *bar = gf100_bar(base); + u32 addr = nvkm_memory_addr(bar->bar[0].inst) >> 12; + if (bar->bar2_halve) + addr |= 0x40000000; + nvkm_wr32(device, 0xb80f48, 0x80000000 | addr); +} + +static void +tu102_bar_bar1_wait(struct nvkm_bar *bar) +{ + struct nvkm_device *device = bar->subdev.device; + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0xb80f50) & 0x00000003)) + break; + ); +} + +static void +tu102_bar_bar1_fini(struct nvkm_bar *bar) +{ + nvkm_mask(bar->subdev.device, 0xb80f40, 0x80000000, 0x00000000); +} + +static void +tu102_bar_bar1_init(struct nvkm_bar *base) +{ + struct nvkm_device *device = base->subdev.device; + struct gf100_bar *bar = gf100_bar(base); + const u32 addr = nvkm_memory_addr(bar->bar[1].inst) >> 12; + nvkm_wr32(device, 0xb80f40, 0x80000000 | addr); +} + +static const struct nvkm_bar_func +tu102_bar = { + .dtor = gf100_bar_dtor, + .oneinit = gf100_bar_oneinit, + .bar1.init = tu102_bar_bar1_init, + .bar1.fini = tu102_bar_bar1_fini, + .bar1.wait = tu102_bar_bar1_wait, + .bar1.vmm = gf100_bar_bar1_vmm, + .bar2.init = tu102_bar_bar2_init, + .bar2.fini = tu102_bar_bar2_fini, + .bar2.wait = tu102_bar_bar2_wait, + .bar2.vmm = gf100_bar_bar2_vmm, + .flush = g84_bar_flush, +}; + +int +tu102_bar_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bar **pbar) +{ + return gf100_bar_new_(&tu102_bar, device, type, inst, pbar); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild new file mode 100644 index 000000000..5a970fb8f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/bios/base.o +nvkm-y += nvkm/subdev/bios/bit.o +nvkm-y += nvkm/subdev/bios/boost.o +nvkm-y += nvkm/subdev/bios/conn.o +nvkm-y += nvkm/subdev/bios/cstep.o +nvkm-y += nvkm/subdev/bios/dcb.o +nvkm-y += nvkm/subdev/bios/disp.o +nvkm-y += nvkm/subdev/bios/dp.o +nvkm-y += nvkm/subdev/bios/extdev.o +nvkm-y += nvkm/subdev/bios/fan.o +nvkm-y += nvkm/subdev/bios/gpio.o +nvkm-y += nvkm/subdev/bios/i2c.o +nvkm-y += nvkm/subdev/bios/iccsense.o +nvkm-y += nvkm/subdev/bios/image.o +nvkm-y += nvkm/subdev/bios/init.o +nvkm-y += nvkm/subdev/bios/mxm.o +nvkm-y += nvkm/subdev/bios/npde.o +nvkm-y += nvkm/subdev/bios/pcir.o +nvkm-y += nvkm/subdev/bios/perf.o +nvkm-y += nvkm/subdev/bios/pll.o +nvkm-y += nvkm/subdev/bios/pmu.o +nvkm-y += nvkm/subdev/bios/power_budget.o +nvkm-y += nvkm/subdev/bios/ramcfg.o +nvkm-y += nvkm/subdev/bios/rammap.o +nvkm-y += nvkm/subdev/bios/shadow.o +nvkm-y += nvkm/subdev/bios/shadowacpi.o +nvkm-y += nvkm/subdev/bios/shadowof.o +nvkm-y += nvkm/subdev/bios/shadowpci.o +nvkm-y += nvkm/subdev/bios/shadowramin.o +nvkm-y += nvkm/subdev/bios/shadowrom.o +nvkm-y += nvkm/subdev/bios/timing.o +nvkm-y += nvkm/subdev/bios/therm.o +nvkm-y += nvkm/subdev/bios/vmap.o +nvkm-y += nvkm/subdev/bios/volt.o +nvkm-y += nvkm/subdev/bios/vpstate.o +nvkm-y += nvkm/subdev/bios/xpio.o +nvkm-y += nvkm/subdev/bios/M0203.o +nvkm-y += nvkm/subdev/bios/M0205.o +nvkm-y += nvkm/subdev/bios/M0209.o +nvkm-y += nvkm/subdev/bios/P0260.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c new file mode 100644 index 000000000..43f0ba1fb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c @@ -0,0 +1,129 @@ +/* + * Copyright 2014 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 +#include +#include + +u32 +nvbios_M0203Te(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_M; + u32 data = 0x00000000; + + if (!bit_entry(bios, 'M', &bit_M)) { + if (bit_M.version == 2 && bit_M.length > 0x04) + data = nvbios_rd16(bios, bit_M.offset + 0x03); + if (data) { + *ver = nvbios_rd08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nvbios_rd08(bios, data + 0x01); + *len = nvbios_rd08(bios, data + 0x02); + *cnt = nvbios_rd08(bios, data + 0x03); + return data; + default: + break; + } + } + } + + return 0x00000000; +} + +u32 +nvbios_M0203Tp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_M0203T *info) +{ + u32 data = nvbios_M0203Te(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->type = nvbios_rd08(bios, data + 0x04); + info->pointer = nvbios_rd16(bios, data + 0x05); + break; + default: + break; + } + return data; +} + +u32 +nvbios_M0203Ee(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr) +{ + u8 cnt, len; + u32 data = nvbios_M0203Te(bios, ver, hdr, &cnt, &len); + if (data && idx < cnt) { + data = data + *hdr + idx * len; + *hdr = len; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0203Ep(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0203E *info) +{ + u32 data = nvbios_M0203Ee(bios, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->type = (nvbios_rd08(bios, data + 0x00) & 0x0f) >> 0; + info->strap = (nvbios_rd08(bios, data + 0x00) & 0xf0) >> 4; + info->group = (nvbios_rd08(bios, data + 0x01) & 0x0f) >> 0; + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_M0203Em(struct nvkm_bios *bios, u8 ramcfg, u8 *ver, u8 *hdr, + struct nvbios_M0203E *info) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvbios_M0203T M0203T; + u8 cnt, len, idx = 0xff; + u32 data; + + if (!nvbios_M0203Tp(bios, ver, hdr, &cnt, &len, &M0203T)) { + nvkm_warn(subdev, "M0203T not found\n"); + return 0x00000000; + } + + while ((data = nvbios_M0203Ep(bios, ++idx, ver, hdr, info))) { + switch (M0203T.type) { + case M0203T_TYPE_RAMCFG: + if (info->strap != ramcfg) + continue; + return data; + default: + nvkm_warn(subdev, "M0203T type %02x\n", M0203T.type); + return 0x00000000; + } + } + + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c new file mode 100644 index 000000000..293a6af1b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c @@ -0,0 +1,135 @@ +/* + * 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 +#include +#include + +u32 +nvbios_M0205Te(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_M; + u32 data = 0x00000000; + + if (!bit_entry(bios, 'M', &bit_M)) { + if (bit_M.version == 2 && bit_M.length > 0x08) + data = nvbios_rd32(bios, bit_M.offset + 0x05); + if (data) { + *ver = nvbios_rd08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nvbios_rd08(bios, data + 0x01); + *len = nvbios_rd08(bios, data + 0x02); + *ssz = nvbios_rd08(bios, data + 0x03); + *snr = nvbios_rd08(bios, data + 0x04); + *cnt = nvbios_rd08(bios, data + 0x05); + return data; + default: + break; + } + } + } + + return 0x00000000; +} + +u32 +nvbios_M0205Tp(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz, + struct nvbios_M0205T *info) +{ + u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, snr, ssz); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->freq = nvbios_rd16(bios, data + 0x06); + break; + default: + break; + } + return data; +} + +u32 +nvbios_M0205Ee(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, &snr, &ssz); + if (data && idx < *cnt) { + data = data + *hdr + idx * (*len + (snr * ssz)); + *hdr = *len; + *cnt = snr; + *len = ssz; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0205Ep(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_M0205E *info) +{ + u32 data = nvbios_M0205Ee(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->type = nvbios_rd08(bios, data + 0x00) & 0x0f; + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_M0205Se(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr) +{ + + u8 cnt, len; + u32 data = nvbios_M0205Ee(bios, ent, ver, hdr, &cnt, &len); + if (data && idx < cnt) { + data = data + *hdr + idx * len; + *hdr = len; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0205Sp(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0205S *info) +{ + u32 data = nvbios_M0205Se(bios, ent, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->data = nvbios_rd08(bios, data + 0x00); + return data; + default: + break; + } + return 0x00000000; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c new file mode 100644 index 000000000..95d49a526 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c @@ -0,0 +1,135 @@ +/* + * 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 +#include +#include + +u32 +nvbios_M0209Te(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_M; + u32 data = 0x00000000; + + if (!bit_entry(bios, 'M', &bit_M)) { + if (bit_M.version == 2 && bit_M.length > 0x0c) + data = nvbios_rd32(bios, bit_M.offset + 0x09); + if (data) { + *ver = nvbios_rd08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nvbios_rd08(bios, data + 0x01); + *len = nvbios_rd08(bios, data + 0x02); + *ssz = nvbios_rd08(bios, data + 0x03); + *snr = 1; + *cnt = nvbios_rd08(bios, data + 0x04); + return data; + default: + break; + } + } + } + + return 0x00000000; +} + +u32 +nvbios_M0209Ee(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 data = nvbios_M0209Te(bios, ver, hdr, cnt, len, &snr, &ssz); + if (data && idx < *cnt) { + data = data + *hdr + idx * (*len + (snr * ssz)); + *hdr = *len; + *cnt = snr; + *len = ssz; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0209Ep(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_M0209E *info) +{ + u32 data = nvbios_M0209Ee(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->v00_40 = (nvbios_rd08(bios, data + 0x00) & 0x40) >> 6; + info->bits = nvbios_rd08(bios, data + 0x00) & 0x3f; + info->modulo = nvbios_rd08(bios, data + 0x01); + info->v02_40 = (nvbios_rd08(bios, data + 0x02) & 0x40) >> 6; + info->v02_07 = nvbios_rd08(bios, data + 0x02) & 0x07; + info->v03 = nvbios_rd08(bios, data + 0x03); + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_M0209Se(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr) +{ + + u8 cnt, len; + u32 data = nvbios_M0209Ee(bios, ent, ver, hdr, &cnt, &len); + if (data && idx < cnt) { + data = data + *hdr + idx * len; + *hdr = len; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0209Sp(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0209S *info) +{ + struct nvbios_M0209E M0209E; + u8 cnt, len; + u32 data = nvbios_M0209Ep(bios, ent, ver, hdr, &cnt, &len, &M0209E); + if (data) { + u32 i, data = nvbios_M0209Se(bios, ent, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + for (i = 0; i < ARRAY_SIZE(info->data); i++) { + u32 bits = (i % M0209E.modulo) * M0209E.bits; + u32 mask = (1ULL << M0209E.bits) - 1; + u16 off = bits / 8; + u8 mod = bits % 8; + info->data[i] = nvbios_rd32(bios, data + off); + info->data[i] = info->data[i] >> mod; + info->data[i] = info->data[i] & mask; + } + return data; + default: + break; + } + } + return 0x00000000; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c new file mode 100644 index 000000000..3f7db3eb3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c @@ -0,0 +1,107 @@ +/* + * 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 +#include +#include + +u32 +nvbios_P0260Te(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz) +{ + struct bit_entry bit_P; + u32 data = 0x00000000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2 && bit_P.length > 0x63) + data = nvbios_rd32(bios, bit_P.offset + 0x60); + if (data) { + *ver = nvbios_rd08(bios, data + 0); + switch (*ver) { + case 0x10: + *hdr = nvbios_rd08(bios, data + 1); + *cnt = nvbios_rd08(bios, data + 2); + *len = 4; + *xnr = nvbios_rd08(bios, data + 3); + *xsz = 4; + return data; + default: + break; + } + } + } + + return 0x00000000; +} + +u32 +nvbios_P0260Ee(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt, xnr, xsz; + u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, len, &xnr, &xsz); + if (data && idx < cnt) + return data + hdr + (idx * *len); + return 0x00000000; +} + +u32 +nvbios_P0260Ep(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, + struct nvbios_P0260E *info) +{ + u32 data = nvbios_P0260Ee(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->data = nvbios_rd32(bios, data); + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_P0260Xe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *xsz) +{ + u8 hdr, cnt, len, xnr; + u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, &len, &xnr, xsz); + if (data && idx < xnr) + return data + hdr + (cnt * len) + (idx * *xsz); + return 0x00000000; +} + +u32 +nvbios_P0260Xp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + struct nvbios_P0260X *info) +{ + u32 data = nvbios_P0260Xe(bios, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->data = nvbios_rd32(bios, data); + return data; + default: + break; + } + return 0x00000000; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c new file mode 100644 index 000000000..6c318e41b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c @@ -0,0 +1,205 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include +#include +#include + +static bool +nvbios_addr(struct nvkm_bios *bios, u32 *addr, u8 size) +{ + u32 p = *addr; + + if (*addr >= bios->image0_size && bios->imaged_addr) { + *addr -= bios->image0_size; + *addr += bios->imaged_addr; + } + + if (unlikely(*addr + size > bios->size)) { + nvkm_error(&bios->subdev, "OOB %d %08x %08x\n", size, p, *addr); + return false; + } + + return true; +} + +u8 +nvbios_rd08(struct nvkm_bios *bios, u32 addr) +{ + if (likely(nvbios_addr(bios, &addr, 1))) + return bios->data[addr]; + return 0x00; +} + +u16 +nvbios_rd16(struct nvkm_bios *bios, u32 addr) +{ + if (likely(nvbios_addr(bios, &addr, 2))) + return get_unaligned_le16(&bios->data[addr]); + return 0x0000; +} + +u32 +nvbios_rd32(struct nvkm_bios *bios, u32 addr) +{ + if (likely(nvbios_addr(bios, &addr, 4))) + return get_unaligned_le32(&bios->data[addr]); + return 0x00000000; +} + +u8 +nvbios_checksum(const u8 *data, int size) +{ + u8 sum = 0; + while (size--) + sum += *data++; + return sum; +} + +u16 +nvbios_findstr(const u8 *data, int size, const char *str, int len) +{ + int i, j; + + for (i = 0; i <= (size - len); i++) { + for (j = 0; j < len; j++) + if ((char)data[i + j] != str[j]) + break; + if (j == len) + return i; + } + + return 0; +} + +int +nvbios_memcmp(struct nvkm_bios *bios, u32 addr, const char *str, u32 len) +{ + unsigned char c1, c2; + + while (len--) { + c1 = nvbios_rd08(bios, addr++); + c2 = *(str++); + if (c1 != c2) + return c1 - c2; + } + return 0; +} + +int +nvbios_extend(struct nvkm_bios *bios, u32 length) +{ + if (bios->size < length) { + u8 *prev = bios->data; + if (!(bios->data = kmalloc(length, GFP_KERNEL))) { + bios->data = prev; + return -ENOMEM; + } + memcpy(bios->data, prev, bios->size); + bios->size = length; + kfree(prev); + return 1; + } + return 0; +} + +static void * +nvkm_bios_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_bios *bios = nvkm_bios(subdev); + kfree(bios->data); + return bios; +} + +static const struct nvkm_subdev_func +nvkm_bios = { + .dtor = nvkm_bios_dtor, +}; + +int +nvkm_bios_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bios **pbios) +{ + struct nvkm_bios *bios; + struct nvbios_image image; + struct bit_entry bit_i; + int ret, idx = 0; + + if (!(bios = *pbios = kzalloc(sizeof(*bios), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_bios, device, type, inst, &bios->subdev); + + ret = nvbios_shadow(bios); + if (ret) + return ret; + + /* Some tables have weird pointers that need adjustment before + * they're dereferenced. I'm not entirely sure why... + */ + if (nvbios_image(bios, idx++, &image)) { + bios->image0_size = image.size; + while (nvbios_image(bios, idx++, &image)) { + if (image.type == 0xe0) { + bios->imaged_addr = image.base; + break; + } + } + } + + /* detect type of vbios we're dealing with */ + bios->bmp_offset = nvbios_findstr(bios->data, bios->size, + "\xff\x7f""NV\0", 5); + if (bios->bmp_offset) { + nvkm_debug(&bios->subdev, "BMP version %x.%x\n", + bmp_version(bios) >> 8, + bmp_version(bios) & 0xff); + } + + bios->bit_offset = nvbios_findstr(bios->data, bios->size, + "\xff\xb8""BIT", 5); + if (bios->bit_offset) + nvkm_debug(&bios->subdev, "BIT signature found\n"); + + /* determine the vbios version number */ + if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) { + bios->version.major = nvbios_rd08(bios, bit_i.offset + 3); + bios->version.chip = nvbios_rd08(bios, bit_i.offset + 2); + bios->version.minor = nvbios_rd08(bios, bit_i.offset + 1); + bios->version.micro = nvbios_rd08(bios, bit_i.offset + 0); + bios->version.patch = nvbios_rd08(bios, bit_i.offset + 4); + } else + if (bmp_version(bios)) { + bios->version.major = nvbios_rd08(bios, bios->bmp_offset + 13); + bios->version.chip = nvbios_rd08(bios, bios->bmp_offset + 12); + bios->version.minor = nvbios_rd08(bios, bios->bmp_offset + 11); + bios->version.micro = nvbios_rd08(bios, bios->bmp_offset + 10); + } + + nvkm_info(&bios->subdev, "version %02x.%02x.%02x.%02x.%02x\n", + bios->version.major, bios->version.chip, + bios->version.minor, bios->version.micro, bios->version.patch); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c new file mode 100644 index 000000000..070ff33f8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c @@ -0,0 +1,49 @@ +/* + * 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 +#include + +int +bit_entry(struct nvkm_bios *bios, u8 id, struct bit_entry *bit) +{ + if (likely(bios->bit_offset)) { + u8 entries = nvbios_rd08(bios, bios->bit_offset + 10); + u32 entry = bios->bit_offset + 12; + while (entries--) { + if (nvbios_rd08(bios, entry + 0) == id) { + bit->id = nvbios_rd08(bios, entry + 0); + bit->version = nvbios_rd08(bios, entry + 1); + bit->length = nvbios_rd16(bios, entry + 2); + bit->offset = nvbios_rd16(bios, entry + 4); + return 0; + } + + entry += nvbios_rd08(bios, bios->bit_offset + 9); + } + + return -ENOENT; + } + + return -EINVAL; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c new file mode 100644 index 000000000..8ab896dd4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c @@ -0,0 +1,126 @@ +/* + * 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 +#include +#include + +u32 +nvbios_boostTe(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_P; + u32 boost = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2 && bit_P.length >= 0x34) + boost = nvbios_rd32(bios, bit_P.offset + 0x30); + + if (boost) { + *ver = nvbios_rd08(bios, boost + 0); + switch (*ver) { + case 0x11: + *hdr = nvbios_rd08(bios, boost + 1); + *cnt = nvbios_rd08(bios, boost + 5); + *len = nvbios_rd08(bios, boost + 2); + *snr = nvbios_rd08(bios, boost + 4); + *ssz = nvbios_rd08(bios, boost + 3); + return boost; + default: + break; + } + } + } + + return 0; +} + +u32 +nvbios_boostEe(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 data = nvbios_boostTe(bios, ver, hdr, cnt, len, &snr, &ssz); + if (data && idx < *cnt) { + data = data + *hdr + (idx * (*len + (snr * ssz))); + *hdr = *len; + *cnt = snr; + *len = ssz; + return data; + } + return 0; +} + +u32 +nvbios_boostEp(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info) +{ + u32 data = nvbios_boostEe(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->pstate = (nvbios_rd16(bios, data + 0x00) & 0x01e0) >> 5; + info->min = nvbios_rd16(bios, data + 0x02) * 1000; + info->max = nvbios_rd16(bios, data + 0x04) * 1000; + } + return data; +} + +u32 +nvbios_boostEm(struct nvkm_bios *bios, u8 pstate, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info) +{ + u32 data, idx = 0; + while ((data = nvbios_boostEp(bios, idx++, ver, hdr, cnt, len, info))) { + if (info->pstate == pstate) + break; + } + return data; +} + +u32 +nvbios_boostSe(struct nvkm_bios *bios, int idx, + u32 data, u8 *ver, u8 *hdr, u8 cnt, u8 len) +{ + if (data && idx < cnt) { + data = data + *hdr + (idx * len); + *hdr = len; + return data; + } + return 0; +} + +u32 +nvbios_boostSp(struct nvkm_bios *bios, int idx, + u32 data, u8 *ver, u8 *hdr, u8 cnt, u8 len, + struct nvbios_boostS *info) +{ + data = nvbios_boostSe(bios, idx, data, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->domain = nvbios_rd08(bios, data + 0x00); + info->percent = nvbios_rd08(bios, data + 0x01); + info->min = nvbios_rd16(bios, data + 0x02) * 1000; + info->max = nvbios_rd16(bios, data + 0x04) * 1000; + } + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c new file mode 100644 index 000000000..276823426 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c @@ -0,0 +1,97 @@ +/* + * 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 +#include +#include + +u32 +nvbios_connTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u32 dcb = dcb_table(bios, ver, hdr, cnt, len); + if (dcb && *ver >= 0x30 && *hdr >= 0x16) { + u32 data = nvbios_rd16(bios, dcb + 0x14); + if (data) { + *ver = nvbios_rd08(bios, data + 0); + *hdr = nvbios_rd08(bios, data + 1); + *cnt = nvbios_rd08(bios, data + 2); + *len = nvbios_rd08(bios, data + 3); + return data; + } + } + return 0x00000000; +} + +u32 +nvbios_connTp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_connT *info) +{ + u32 data = nvbios_connTe(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x30: + case 0x40: + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_connEe(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u32 data = nvbios_connTe(bios, ver, &hdr, &cnt, len); + if (data && idx < cnt) + return data + hdr + (idx * *len); + return 0x00000000; +} + +u32 +nvbios_connEp(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, + struct nvbios_connE *info) +{ + u32 data = nvbios_connEe(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x30: + case 0x40: + info->type = nvbios_rd08(bios, data + 0x00); + info->location = nvbios_rd08(bios, data + 0x01) & 0x0f; + info->hpd = (nvbios_rd08(bios, data + 0x01) & 0x30) >> 4; + info->dp = (nvbios_rd08(bios, data + 0x01) & 0xc0) >> 6; + if (*len < 4) + return data; + info->hpd |= (nvbios_rd08(bios, data + 0x02) & 0x03) << 2; + info->dp |= nvbios_rd08(bios, data + 0x02) & 0x0c; + info->di = (nvbios_rd08(bios, data + 0x02) & 0xf0) >> 4; + info->hpd |= (nvbios_rd08(bios, data + 0x03) & 0x07) << 4; + info->sr = (nvbios_rd08(bios, data + 0x03) & 0x08) >> 3; + info->lcdid = (nvbios_rd08(bios, data + 0x03) & 0x70) >> 4; + return data; + default: + break; + } + return 0x00000000; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c new file mode 100644 index 000000000..7c8c36054 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c @@ -0,0 +1,122 @@ +/* + * 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 +#include +#include + +u32 +nvbios_cstepTe(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz) +{ + struct bit_entry bit_P; + u32 cstep = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2 && bit_P.length >= 0x38) + cstep = nvbios_rd32(bios, bit_P.offset + 0x34); + + if (cstep) { + *ver = nvbios_rd08(bios, cstep + 0); + switch (*ver) { + case 0x10: + *hdr = nvbios_rd08(bios, cstep + 1); + *cnt = nvbios_rd08(bios, cstep + 3); + *len = nvbios_rd08(bios, cstep + 2); + *xnr = nvbios_rd08(bios, cstep + 5); + *xsz = nvbios_rd08(bios, cstep + 4); + return cstep; + default: + break; + } + } + } + + return 0; +} + +u32 +nvbios_cstepEe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr) +{ + u8 cnt, len, xnr, xsz; + u32 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz); + if (data && idx < cnt) { + data = data + *hdr + (idx * len); + *hdr = len; + return data; + } + return 0; +} + +u32 +nvbios_cstepEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + struct nvbios_cstepE *info) +{ + u32 data = nvbios_cstepEe(bios, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->pstate = (nvbios_rd16(bios, data + 0x00) & 0x01e0) >> 5; + info->index = nvbios_rd08(bios, data + 0x03); + } + return data; +} + +u32 +nvbios_cstepEm(struct nvkm_bios *bios, u8 pstate, u8 *ver, u8 *hdr, + struct nvbios_cstepE *info) +{ + u32 data, idx = 0; + while ((data = nvbios_cstepEp(bios, idx++, ver, hdr, info))) { + if (info->pstate == pstate) + break; + } + return data; +} + +u32 +nvbios_cstepXe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr) +{ + u8 cnt, len, xnr, xsz; + u32 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz); + if (data && idx < xnr) { + data = data + *hdr + (cnt * len) + (idx * xsz); + *hdr = xsz; + return data; + } + return 0; +} + +u32 +nvbios_cstepXp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + struct nvbios_cstepX *info) +{ + u32 data = nvbios_cstepXe(bios, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->freq = nvbios_rd16(bios, data + 0x00) * 1000; + info->unkn[0] = nvbios_rd08(bios, data + 0x02); + info->unkn[1] = nvbios_rd08(bios, data + 0x03); + info->voltage = nvbios_rd08(bios, data + 0x04); + } + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c new file mode 100644 index 000000000..8698f260b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c @@ -0,0 +1,235 @@ +/* + * 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 +#include + +u16 +dcb_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvkm_device *device = subdev->device; + u16 dcb = 0x0000; + + if (device->card_type > NV_04) + dcb = nvbios_rd16(bios, 0x36); + if (!dcb) { + nvkm_warn(subdev, "DCB table not found\n"); + return dcb; + } + + *ver = nvbios_rd08(bios, dcb); + + if (*ver >= 0x42) { + nvkm_warn(subdev, "DCB version 0x%02x unknown\n", *ver); + return 0x0000; + } else + if (*ver >= 0x30) { + if (nvbios_rd32(bios, dcb + 6) == 0x4edcbdcb) { + *hdr = nvbios_rd08(bios, dcb + 1); + *cnt = nvbios_rd08(bios, dcb + 2); + *len = nvbios_rd08(bios, dcb + 3); + return dcb; + } + } else + if (*ver >= 0x20) { + if (nvbios_rd32(bios, dcb + 4) == 0x4edcbdcb) { + u16 i2c = nvbios_rd16(bios, dcb + 2); + *hdr = 8; + *cnt = (i2c - dcb) / 8; + *len = 8; + return dcb; + } + } else + if (*ver >= 0x15) { + if (!nvbios_memcmp(bios, dcb - 7, "DEV_REC", 7)) { + u16 i2c = nvbios_rd16(bios, dcb + 2); + *hdr = 4; + *cnt = (i2c - dcb) / 10; + *len = 10; + return dcb; + } + } else { + /* + * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but + * always has the same single (crt) entry, even when tv-out + * present, so the conclusion is this version cannot really + * be used. + * + * v1.2 tables (some NV6/10, and NV15+) normally have the + * same 5 entries, which are not specific to the card and so + * no use. + * + * v1.2 does have an I2C table that read_dcb_i2c_table can + * handle, but cards exist (nv11 in #14821) with a bad i2c + * table pointer, so use the indices parsed in + * parse_bmp_structure. + * + * v1.1 (NV5+, maybe some NV4) is entirely unhelpful + */ + nvkm_debug(subdev, "DCB contains no useful data\n"); + return 0x0000; + } + + nvkm_warn(subdev, "DCB header validation failed\n"); + return 0x0000; +} + +u16 +dcb_outp(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 dcb = dcb_table(bios, ver, &hdr, &cnt, len); + if (dcb && idx < cnt) + return dcb + hdr + (idx * *len); + return 0x0000; +} + +static inline u16 +dcb_outp_hasht(struct dcb_output *outp) +{ + return (outp->extdev << 8) | (outp->location << 4) | outp->type; +} + +static inline u16 +dcb_outp_hashm(struct dcb_output *outp) +{ + return (outp->heads << 8) | (outp->link << 6) | outp->or; +} + +u16 +dcb_outp_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, + struct dcb_output *outp) +{ + u16 dcb = dcb_outp(bios, idx, ver, len); + memset(outp, 0x00, sizeof(*outp)); + if (dcb) { + if (*ver >= 0x20) { + u32 conn = nvbios_rd32(bios, dcb + 0x00); + outp->or = (conn & 0x0f000000) >> 24; + outp->location = (conn & 0x00300000) >> 20; + outp->bus = (conn & 0x000f0000) >> 16; + outp->connector = (conn & 0x0000f000) >> 12; + outp->heads = (conn & 0x00000f00) >> 8; + outp->i2c_index = (conn & 0x000000f0) >> 4; + outp->type = (conn & 0x0000000f); + outp->link = 0; + } else { + dcb = 0x0000; + } + + if (*ver >= 0x40) { + u32 conf = nvbios_rd32(bios, dcb + 0x04); + switch (outp->type) { + case DCB_OUTPUT_DP: + switch (conf & 0x00e00000) { + case 0x00000000: /* 1.62 */ + outp->dpconf.link_bw = 0x06; + break; + case 0x00200000: /* 2.7 */ + outp->dpconf.link_bw = 0x0a; + break; + case 0x00400000: /* 5.4 */ + outp->dpconf.link_bw = 0x14; + break; + case 0x00600000: /* 8.1 */ + default: + outp->dpconf.link_bw = 0x1e; + break; + } + + switch ((conf & 0x0f000000) >> 24) { + case 0xf: + case 0x4: + outp->dpconf.link_nr = 4; + break; + case 0x3: + case 0x2: + outp->dpconf.link_nr = 2; + break; + case 0x1: + default: + outp->dpconf.link_nr = 1; + break; + } + fallthrough; + + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + outp->link = (conf & 0x00000030) >> 4; + outp->sorconf.link = outp->link; /*XXX*/ + outp->extdev = 0x00; + if (outp->location != 0) + outp->extdev = (conf & 0x0000ff00) >> 8; + break; + default: + break; + } + } + + outp->hasht = dcb_outp_hasht(outp); + outp->hashm = dcb_outp_hashm(outp); + } + return dcb; +} + +u16 +dcb_outp_match(struct nvkm_bios *bios, u16 type, u16 mask, + u8 *ver, u8 *len, struct dcb_output *outp) +{ + u16 dcb, idx = 0; + while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) { + if ((dcb_outp_hasht(outp) & 0x00ff) == (type & 0x00ff)) { + if ((dcb_outp_hashm(outp) & mask) == mask) + break; + } + } + return dcb; +} + +int +dcb_outp_foreach(struct nvkm_bios *bios, void *data, + int (*exec)(struct nvkm_bios *, void *, int, u16)) +{ + int ret, idx = -1; + u8 ver, len; + u16 outp; + + while ((outp = dcb_outp(bios, ++idx, &ver, &len))) { + if (nvbios_rd32(bios, outp) == 0x00000000) + break; /* seen on an NV11 with DCB v1.5 */ + if (nvbios_rd32(bios, outp) == 0xffffffff) + break; /* seen on an NV17 with DCB v2.0 */ + + if (nvbios_rd08(bios, outp) == DCB_OUTPUT_UNUSED) + continue; + if (nvbios_rd08(bios, outp) == DCB_OUTPUT_EOL) + break; + + ret = exec(bios, data, idx, outp); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c new file mode 100644 index 000000000..9efb1b48c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c @@ -0,0 +1,174 @@ +/* + * 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 +#include +#include + +u16 +nvbios_disp_table(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub) +{ + struct bit_entry U; + + if (!bit_entry(bios, 'U', &U)) { + if (U.version == 1) { + u16 data = nvbios_rd16(bios, U.offset); + if (data) { + *ver = nvbios_rd08(bios, data + 0x00); + switch (*ver) { + case 0x20: + case 0x21: + case 0x22: + *hdr = nvbios_rd08(bios, data + 0x01); + *len = nvbios_rd08(bios, data + 0x02); + *cnt = nvbios_rd08(bios, data + 0x03); + *sub = nvbios_rd08(bios, data + 0x04); + return data; + default: + break; + } + } + } + } + + return 0x0000; +} + +u16 +nvbios_disp_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub) +{ + u8 hdr, cnt; + u16 data = nvbios_disp_table(bios, ver, &hdr, &cnt, len, sub); + if (data && idx < cnt) + return data + hdr + (idx * *len); + *ver = 0x00; + return 0x0000; +} + +u16 +nvbios_disp_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub, + struct nvbios_disp *info) +{ + u16 data = nvbios_disp_entry(bios, idx, ver, len, sub); + if (data && *len >= 2) { + info->data = nvbios_rd16(bios, data + 0); + return data; + } + return 0x0000; +} + +u16 +nvbios_outp_entry(struct nvkm_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct nvbios_disp info; + u16 data = nvbios_disp_parse(bios, idx, ver, len, hdr, &info); + if (data) { + *cnt = nvbios_rd08(bios, info.data + 0x05); + *len = 0x06; + data = info.data; + } + return data; +} + +u16 +nvbios_outp_parse(struct nvkm_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) +{ + u16 data = nvbios_outp_entry(bios, idx, ver, hdr, cnt, len); + if (data && *hdr >= 0x0a) { + info->type = nvbios_rd16(bios, data + 0x00); + info->mask = nvbios_rd32(bios, data + 0x02); + if (*ver <= 0x20) /* match any link */ + info->mask |= 0x00c0; + info->script[0] = nvbios_rd16(bios, data + 0x06); + info->script[1] = nvbios_rd16(bios, data + 0x08); + info->script[2] = 0x0000; + if (*hdr >= 0x0c) + info->script[2] = nvbios_rd16(bios, data + 0x0a); + return data; + } + return 0x0000; +} + +u16 +nvbios_outp_match(struct nvkm_bios *bios, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) +{ + u16 data, idx = 0; + while ((data = nvbios_outp_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) { + if (data && info->type == type) { + if ((info->mask & mask) == mask) + break; + } + } + return data; +} + +u16 +nvbios_ocfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + if (idx < *cnt) + return outp + *hdr + (idx * *len); + return 0x0000; +} + +u16 +nvbios_ocfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info) +{ + u16 data = nvbios_ocfg_entry(bios, outp, idx, ver, hdr, cnt, len); + if (data) { + info->proto = nvbios_rd08(bios, data + 0x00); + info->flags = nvbios_rd16(bios, data + 0x01); + info->clkcmp[0] = nvbios_rd16(bios, data + 0x02); + info->clkcmp[1] = nvbios_rd16(bios, data + 0x04); + } + return data; +} + +u16 +nvbios_ocfg_match(struct nvkm_bios *bios, u16 outp, u8 proto, u8 flags, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info) +{ + u16 data, idx = 0; + while ((data = nvbios_ocfg_parse(bios, outp, idx++, ver, hdr, cnt, len, info))) { + if ((info->proto == proto || info->proto == 0xff) && + (info->flags == flags)) + break; + } + return data; +} + +u16 +nvbios_oclk_match(struct nvkm_bios *bios, u16 cmp, u32 khz) +{ + while (cmp) { + if (khz / 10 >= nvbios_rd16(bios, cmp + 0x00)) + return nvbios_rd16(bios, cmp + 0x02); + cmp += 0x04; + } + return 0x0000; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c new file mode 100644 index 000000000..c694501ae --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c @@ -0,0 +1,232 @@ +/* + * 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 +#include +#include + +u16 +nvbios_dp_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry d; + + if (!bit_entry(bios, 'd', &d)) { + if (d.version == 1 && d.length >= 2) { + u16 data = nvbios_rd16(bios, d.offset); + if (data) { + *ver = nvbios_rd08(bios, data + 0x00); + switch (*ver) { + case 0x20: + case 0x21: + case 0x30: + case 0x40: + case 0x41: + case 0x42: + *hdr = nvbios_rd08(bios, data + 0x01); + *len = nvbios_rd08(bios, data + 0x02); + *cnt = nvbios_rd08(bios, data + 0x03); + return data; + default: + break; + } + } + } + } + + return 0x0000; +} + +static u16 +nvbios_dpout_entry(struct nvkm_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len); + if (data && idx < *cnt) { + u16 outp = nvbios_rd16(bios, data + *hdr + idx * *len); + switch (*ver * !!outp) { + case 0x20: + case 0x21: + case 0x30: + *hdr = nvbios_rd08(bios, data + 0x04); + *len = nvbios_rd08(bios, data + 0x05); + *cnt = nvbios_rd08(bios, outp + 0x04); + break; + case 0x40: + case 0x41: + case 0x42: + *hdr = nvbios_rd08(bios, data + 0x04); + *cnt = 0; + *len = 0; + break; + default: + break; + } + return outp; + } + *ver = 0x00; + return 0x0000; +} + +u16 +nvbios_dpout_parse(struct nvkm_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *info) +{ + u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + if (data && *ver) { + info->type = nvbios_rd16(bios, data + 0x00); + info->mask = nvbios_rd16(bios, data + 0x02); + switch (*ver) { + case 0x20: + info->mask |= 0x00c0; /* match any link */ + fallthrough; + case 0x21: + case 0x30: + info->flags = nvbios_rd08(bios, data + 0x05); + info->script[0] = nvbios_rd16(bios, data + 0x06); + info->script[1] = nvbios_rd16(bios, data + 0x08); + if (*len >= 0x0c) + info->lnkcmp = nvbios_rd16(bios, data + 0x0a); + if (*len >= 0x0f) { + info->script[2] = nvbios_rd16(bios, data + 0x0c); + info->script[3] = nvbios_rd16(bios, data + 0x0e); + } + if (*len >= 0x11) + info->script[4] = nvbios_rd16(bios, data + 0x10); + break; + case 0x40: + case 0x41: + case 0x42: + info->flags = nvbios_rd08(bios, data + 0x04); + info->script[0] = nvbios_rd16(bios, data + 0x05); + info->script[1] = nvbios_rd16(bios, data + 0x07); + info->lnkcmp = nvbios_rd16(bios, data + 0x09); + info->script[2] = nvbios_rd16(bios, data + 0x0b); + info->script[3] = nvbios_rd16(bios, data + 0x0d); + info->script[4] = nvbios_rd16(bios, data + 0x0f); + break; + default: + data = 0x0000; + break; + } + } + return data; +} + +u16 +nvbios_dpout_match(struct nvkm_bios *bios, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *info) +{ + u16 data, idx = 0; + while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) { + if (data && info->type == type) { + if ((info->mask & mask) == mask) + break; + } + } + return data; +} + +static u16 +nvbios_dpcfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + if (*ver >= 0x40) { + outp = nvbios_dp_table(bios, ver, hdr, cnt, len); + *hdr = *hdr + (*len * * cnt); + *len = nvbios_rd08(bios, outp + 0x06); + *cnt = nvbios_rd08(bios, outp + 0x07) * + nvbios_rd08(bios, outp + 0x05); + } + + if (idx < *cnt) + return outp + *hdr + (idx * *len); + + return 0x0000; +} + +u16 +nvbios_dpcfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpcfg *info) +{ + u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + if (data) { + switch (*ver) { + case 0x20: + case 0x21: + info->dc = nvbios_rd08(bios, data + 0x02); + info->pe = nvbios_rd08(bios, data + 0x03); + info->tx_pu = nvbios_rd08(bios, data + 0x04); + break; + case 0x30: + case 0x40: + case 0x41: + info->pc = nvbios_rd08(bios, data + 0x00); + info->dc = nvbios_rd08(bios, data + 0x01); + info->pe = nvbios_rd08(bios, data + 0x02); + info->tx_pu = nvbios_rd08(bios, data + 0x03); + break; + case 0x42: + info->dc = nvbios_rd08(bios, data + 0x00); + info->pe = nvbios_rd08(bios, data + 0x01); + info->tx_pu = nvbios_rd08(bios, data + 0x02); + break; + default: + data = 0x0000; + break; + } + } + return data; +} + +u16 +nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpcfg *info) +{ + u8 idx = 0xff; + u16 data; + + if (*ver >= 0x30) { + static const u8 vsoff[] = { 0, 4, 7, 9 }; + idx = (pc * 10) + vsoff[vs] + pe; + if (*ver >= 0x40 && *ver <= 0x41 && *hdr >= 0x12) + idx += nvbios_rd08(bios, outp + 0x11) * 40; + else + if (*ver >= 0x42) + idx += nvbios_rd08(bios, outp + 0x11) * 10; + } else { + while ((data = nvbios_dpcfg_entry(bios, outp, ++idx, + ver, hdr, cnt, len))) { + if (nvbios_rd08(bios, data + 0x00) == vs && + nvbios_rd08(bios, data + 0x01) == pe) + break; + } + } + + return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c new file mode 100644 index 000000000..118e33174 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + */ +#include +#include +#include + +static u16 +extdev_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) +{ + u8 dcb_ver, dcb_hdr, dcb_cnt, dcb_len; + u16 dcb, extdev = 0; + + dcb = dcb_table(bios, &dcb_ver, &dcb_hdr, &dcb_cnt, &dcb_len); + if (!dcb || (dcb_ver != 0x30 && dcb_ver != 0x40 && dcb_ver != 0x41)) + return 0x0000; + + extdev = nvbios_rd16(bios, dcb + 18); + if (!extdev) + return 0x0000; + + *ver = nvbios_rd08(bios, extdev + 0); + *hdr = nvbios_rd08(bios, extdev + 1); + *cnt = nvbios_rd08(bios, extdev + 2); + *len = nvbios_rd08(bios, extdev + 3); + return extdev + *hdr; +} + +bool +nvbios_extdev_skip_probe(struct nvkm_bios *bios) +{ + u8 ver, hdr, len, cnt; + u16 data = extdev_table(bios, &ver, &hdr, &len, &cnt); + if (data && ver == 0x40 && hdr >= 5) { + u8 flags = nvbios_rd08(bios, data - hdr + 4); + if (flags & 1) + return true; + } + return false; +} + +static u16 +nvbios_extdev_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 extdev = extdev_table(bios, ver, &hdr, len, &cnt); + if (extdev && idx < cnt) + return extdev + idx * *len; + return 0x0000; +} + +static void +extdev_parse_entry(struct nvkm_bios *bios, u16 offset, + struct nvbios_extdev_func *entry) +{ + entry->type = nvbios_rd08(bios, offset + 0); + entry->addr = nvbios_rd08(bios, offset + 1); + entry->bus = (nvbios_rd08(bios, offset + 2) >> 4) & 1; +} + +int +nvbios_extdev_parse(struct nvkm_bios *bios, int idx, + struct nvbios_extdev_func *func) +{ + u8 ver, len; + u16 entry; + + if (!(entry = nvbios_extdev_entry(bios, idx, &ver, &len))) + return -EINVAL; + + extdev_parse_entry(bios, entry, func); + return 0; +} + +int +nvbios_extdev_find(struct nvkm_bios *bios, enum nvbios_extdev_type type, + struct nvbios_extdev_func *func) +{ + u8 ver, len, i; + u16 entry; + + i = 0; + while ((entry = nvbios_extdev_entry(bios, i++, &ver, &len))) { + extdev_parse_entry(bios, entry, func); + if (func->type == type) + return 0; + } + + return -EINVAL; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c new file mode 100644 index 000000000..0dfb15a27 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Martin Peres + * + * 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: Martin Peres + */ +#include +#include +#include + +static u32 +nvbios_fan_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_P; + u32 fan = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2 && bit_P.length >= 0x5c) + fan = nvbios_rd32(bios, bit_P.offset + 0x58); + + if (fan) { + *ver = nvbios_rd08(bios, fan + 0); + switch (*ver) { + case 0x10: + *hdr = nvbios_rd08(bios, fan + 1); + *len = nvbios_rd08(bios, fan + 2); + *cnt = nvbios_rd08(bios, fan + 3); + return fan; + default: + break; + } + } + } + + return 0; +} + +static u32 +nvbios_fan_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len) +{ + u32 data = nvbios_fan_table(bios, ver, hdr, cnt, len); + if (data && idx < *cnt) + return data + *hdr + (idx * (*len)); + return 0; +} + +u32 +nvbios_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan) +{ + u8 ver, hdr, cnt, len; + + u32 data = nvbios_fan_entry(bios, 0, &ver, &hdr, &cnt, &len); + if (data) { + u8 type = nvbios_rd08(bios, data + 0x00); + switch (type) { + case 0: + fan->type = NVBIOS_THERM_FAN_TOGGLE; + break; + case 1: + case 2: + /* TODO: Understand the difference between the two! */ + fan->type = NVBIOS_THERM_FAN_PWM; + break; + default: + fan->type = NVBIOS_THERM_FAN_UNK; + } + + fan->fan_mode = NVBIOS_THERM_FAN_LINEAR; + fan->min_duty = nvbios_rd08(bios, data + 0x02); + fan->max_duty = nvbios_rd08(bios, data + 0x03); + + fan->pwm_freq = nvbios_rd32(bios, data + 0x0b) & 0xffffff; + } + + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c new file mode 100644 index 000000000..2107b5584 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c @@ -0,0 +1,150 @@ +/* + * 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 +#include +#include +#include + +u16 +dcb_gpio_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 data = 0x0000; + u16 dcb = dcb_table(bios, ver, hdr, cnt, len); + if (dcb) { + if (*ver >= 0x30 && *hdr >= 0x0c) + data = nvbios_rd16(bios, dcb + 0x0a); + else + if (*ver >= 0x22 && nvbios_rd08(bios, dcb - 1) >= 0x13) + data = nvbios_rd16(bios, dcb - 0x0f); + + if (data) { + *ver = nvbios_rd08(bios, data + 0x00); + if (*ver < 0x30) { + *hdr = 3; + *cnt = nvbios_rd08(bios, data + 0x02); + *len = nvbios_rd08(bios, data + 0x01); + } else + if (*ver <= 0x41) { + *hdr = nvbios_rd08(bios, data + 0x01); + *cnt = nvbios_rd08(bios, data + 0x02); + *len = nvbios_rd08(bios, data + 0x03); + } else { + data = 0x0000; + } + } + } + return data; +} + +u16 +dcb_gpio_entry(struct nvkm_bios *bios, int idx, int ent, u8 *ver, u8 *len) +{ + u8 hdr, cnt, xver; /* use gpio version for xpio entry parsing */ + u16 gpio; + + if (!idx--) + gpio = dcb_gpio_table(bios, ver, &hdr, &cnt, len); + else + gpio = dcb_xpio_table(bios, idx, &xver, &hdr, &cnt, len); + + if (gpio && ent < cnt) + return gpio + hdr + (ent * *len); + + return 0x0000; +} + +u16 +dcb_gpio_parse(struct nvkm_bios *bios, int idx, int ent, u8 *ver, u8 *len, + struct dcb_gpio_func *gpio) +{ + u16 data = dcb_gpio_entry(bios, idx, ent, ver, len); + if (data) { + if (*ver < 0x40) { + u16 info = nvbios_rd16(bios, data); + *gpio = (struct dcb_gpio_func) { + .line = (info & 0x001f) >> 0, + .func = (info & 0x07e0) >> 5, + .log[0] = (info & 0x1800) >> 11, + .log[1] = (info & 0x6000) >> 13, + .param = !!(info & 0x8000), + }; + } else + if (*ver < 0x41) { + u32 info = nvbios_rd32(bios, data); + *gpio = (struct dcb_gpio_func) { + .line = (info & 0x0000001f) >> 0, + .func = (info & 0x0000ff00) >> 8, + .log[0] = (info & 0x18000000) >> 27, + .log[1] = (info & 0x60000000) >> 29, + .param = !!(info & 0x80000000), + }; + } else { + u32 info = nvbios_rd32(bios, data + 0); + u8 info1 = nvbios_rd32(bios, data + 4); + *gpio = (struct dcb_gpio_func) { + .line = (info & 0x0000003f) >> 0, + .func = (info & 0x0000ff00) >> 8, + .log[0] = (info1 & 0x30) >> 4, + .log[1] = (info1 & 0xc0) >> 6, + .param = !!(info & 0x80000000), + }; + } + } + + return data; +} + +u16 +dcb_gpio_match(struct nvkm_bios *bios, int idx, u8 func, u8 line, + u8 *ver, u8 *len, struct dcb_gpio_func *gpio) +{ + u8 hdr, cnt, i = 0; + u16 data; + + while ((data = dcb_gpio_parse(bios, idx, i++, ver, len, gpio))) { + if ((line == 0xff || line == gpio->line) && + (func == 0xff || func == gpio->func)) + return data; + } + + /* DCB 2.2, fixed TVDAC GPIO data */ + if ((data = dcb_table(bios, ver, &hdr, &cnt, len))) { + if (*ver >= 0x22 && *ver < 0x30 && func == DCB_GPIO_TVDAC0) { + u8 conf = nvbios_rd08(bios, data - 5); + u8 addr = nvbios_rd08(bios, data - 4); + if (conf & 0x01) { + *gpio = (struct dcb_gpio_func) { + .func = DCB_GPIO_TVDAC0, + .line = addr >> 4, + .log[0] = !!(conf & 0x02), + .log[1] = !(conf & 0x02), + }; + *ver = 0x00; + return data; + } + } + } + + return 0x0000; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c new file mode 100644 index 000000000..0fc60be32 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c @@ -0,0 +1,164 @@ +/* + * 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 +#include +#include + +u16 +dcb_i2c_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 i2c = 0x0000; + u16 dcb = dcb_table(bios, ver, hdr, cnt, len); + if (dcb) { + if (*ver >= 0x15) + i2c = nvbios_rd16(bios, dcb + 2); + if (*ver >= 0x30) + i2c = nvbios_rd16(bios, dcb + 4); + } + + if (i2c && *ver >= 0x42) { + nvkm_warn(&bios->subdev, "ccb %02x not supported\n", *ver); + return 0x0000; + } + + if (i2c && *ver >= 0x30) { + *ver = nvbios_rd08(bios, i2c + 0); + *hdr = nvbios_rd08(bios, i2c + 1); + *cnt = nvbios_rd08(bios, i2c + 2); + *len = nvbios_rd08(bios, i2c + 3); + } else { + *ver = *ver; /* use DCB version */ + *hdr = 0; + *cnt = 16; + *len = 4; + } + + return i2c; +} + +u16 +dcb_i2c_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 i2c = dcb_i2c_table(bios, ver, &hdr, &cnt, len); + if (i2c && idx < cnt) + return i2c + hdr + (idx * *len); + return 0x0000; +} + +int +dcb_i2c_parse(struct nvkm_bios *bios, u8 idx, struct dcb_i2c_entry *info) +{ + struct nvkm_subdev *subdev = &bios->subdev; + u8 ver, len; + u16 ent = dcb_i2c_entry(bios, idx, &ver, &len); + if (ent) { + if (ver >= 0x41) { + u32 ent_value = nvbios_rd32(bios, ent); + u8 i2c_port = (ent_value >> 0) & 0x1f; + u8 dpaux_port = (ent_value >> 5) & 0x1f; + /* value 0x1f means unused according to DCB 4.x spec */ + if (i2c_port == 0x1f && dpaux_port == 0x1f) + info->type = DCB_I2C_UNUSED; + else + info->type = DCB_I2C_PMGR; + } else + if (ver >= 0x30) { + info->type = nvbios_rd08(bios, ent + 0x03); + } else { + info->type = nvbios_rd08(bios, ent + 0x03) & 0x07; + if (info->type == 0x07) + info->type = DCB_I2C_UNUSED; + } + + info->drive = DCB_I2C_UNUSED; + info->sense = DCB_I2C_UNUSED; + info->share = DCB_I2C_UNUSED; + info->auxch = DCB_I2C_UNUSED; + + switch (info->type) { + case DCB_I2C_NV04_BIT: + info->drive = nvbios_rd08(bios, ent + 0); + info->sense = nvbios_rd08(bios, ent + 1); + return 0; + case DCB_I2C_NV4E_BIT: + info->drive = nvbios_rd08(bios, ent + 1); + return 0; + case DCB_I2C_NVIO_BIT: + info->drive = nvbios_rd08(bios, ent + 0) & 0x0f; + if (nvbios_rd08(bios, ent + 1) & 0x01) + info->share = nvbios_rd08(bios, ent + 1) >> 1; + return 0; + case DCB_I2C_NVIO_AUX: + info->auxch = nvbios_rd08(bios, ent + 0) & 0x0f; + if (nvbios_rd08(bios, ent + 1) & 0x01) + info->share = info->auxch; + return 0; + case DCB_I2C_PMGR: + info->drive = (nvbios_rd16(bios, ent + 0) & 0x01f) >> 0; + if (info->drive == 0x1f) + info->drive = DCB_I2C_UNUSED; + info->auxch = (nvbios_rd16(bios, ent + 0) & 0x3e0) >> 5; + if (info->auxch == 0x1f) + info->auxch = DCB_I2C_UNUSED; + info->share = info->auxch; + return 0; + case DCB_I2C_UNUSED: + return 0; + default: + nvkm_warn(subdev, "unknown i2c type %d\n", info->type); + info->type = DCB_I2C_UNUSED; + return 0; + } + } + + if (bios->bmp_offset && idx < 2) { + /* BMP (from v4.0 has i2c info in the structure, it's in a + * fixed location on earlier VBIOS + */ + if (nvbios_rd08(bios, bios->bmp_offset + 5) < 4) + ent = 0x0048; + else + ent = 0x0036 + bios->bmp_offset; + + if (idx == 0) { + info->drive = nvbios_rd08(bios, ent + 4); + if (!info->drive) info->drive = 0x3f; + info->sense = nvbios_rd08(bios, ent + 5); + if (!info->sense) info->sense = 0x3e; + } else + if (idx == 1) { + info->drive = nvbios_rd08(bios, ent + 6); + if (!info->drive) info->drive = 0x37; + info->sense = nvbios_rd08(bios, ent + 7); + if (!info->sense) info->sense = 0x36; + } + + info->type = DCB_I2C_NV04_BIT; + info->share = DCB_I2C_UNUSED; + return 0; + } + + return -ENOENT; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c new file mode 100644 index 000000000..dea444d48 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c @@ -0,0 +1,129 @@ +/* + * Copyright 2015 Martin Peres + * + * 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: Martin Peres + */ +#include +#include +#include +#include + +static u32 +nvbios_iccsense_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, + u8 *len) +{ + struct bit_entry bit_P; + u32 iccsense; + + if (bit_entry(bios, 'P', &bit_P) || bit_P.version != 2 || + bit_P.length < 0x2c) + return 0; + + iccsense = nvbios_rd32(bios, bit_P.offset + 0x28); + if (!iccsense) + return 0; + + *ver = nvbios_rd08(bios, iccsense + 0); + switch (*ver) { + case 0x10: + case 0x20: + *hdr = nvbios_rd08(bios, iccsense + 1); + *len = nvbios_rd08(bios, iccsense + 2); + *cnt = nvbios_rd08(bios, iccsense + 3); + return iccsense; + default: + break; + } + + return 0; +} + +int +nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense) +{ + struct nvkm_subdev *subdev = &bios->subdev; + u8 ver, hdr, cnt, len, i; + u32 table, entry; + + table = nvbios_iccsense_table(bios, &ver, &hdr, &cnt, &len); + if (!table || !cnt) + return -EINVAL; + + if (ver != 0x10 && ver != 0x20) { + nvkm_error(subdev, "ICCSENSE version 0x%02x unknown\n", ver); + return -EINVAL; + } + + iccsense->nr_entry = cnt; + iccsense->rail = kmalloc_array(cnt, sizeof(struct pwr_rail_t), + GFP_KERNEL); + if (!iccsense->rail) + return -ENOMEM; + + for (i = 0; i < cnt; ++i) { + struct nvbios_extdev_func extdev; + struct pwr_rail_t *rail = &iccsense->rail[i]; + u8 res_start = 0; + int r; + + entry = table + hdr + i * len; + + switch(ver) { + case 0x10: + if ((nvbios_rd08(bios, entry + 0x1) & 0xf8) == 0xf8) + rail->mode = 1; + else + rail->mode = 0; + rail->extdev_id = nvbios_rd08(bios, entry + 0x2); + res_start = 0x3; + break; + case 0x20: + rail->mode = nvbios_rd08(bios, entry); + rail->extdev_id = nvbios_rd08(bios, entry + 0x1); + res_start = 0x5; + break; + } + + if (nvbios_extdev_parse(bios, rail->extdev_id, &extdev)) + continue; + + switch (extdev.type) { + case NVBIOS_EXTDEV_INA209: + case NVBIOS_EXTDEV_INA219: + rail->resistor_count = 1; + break; + case NVBIOS_EXTDEV_INA3221: + rail->resistor_count = 3; + break; + default: + rail->resistor_count = 0; + break; + } + + for (r = 0; r < rail->resistor_count; ++r) { + rail->resistors[r].mohm = nvbios_rd08(bios, entry + res_start + r * 2); + rail->resistors[r].enabled = !(nvbios_rd08(bios, entry + res_start + r * 2 + 1) & 0x40); + } + rail->config = nvbios_rd16(bios, entry + res_start + rail->resistor_count * 2); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c new file mode 100644 index 000000000..1dbff7aea --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c @@ -0,0 +1,83 @@ +/* + * Copyright 2014 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 +#include +#include +#include + +static bool +nvbios_imagen(struct nvkm_bios *bios, struct nvbios_image *image) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvbios_pcirT pcir; + struct nvbios_npdeT npde; + u8 ver; + u16 hdr; + u32 data; + + switch ((data = nvbios_rd16(bios, image->base + 0x00))) { + case 0xaa55: + case 0xbb77: + case 0x4e56: /* NV */ + break; + default: + nvkm_debug(subdev, "%08x: ROM signature (%04x) unknown\n", + image->base, data); + return false; + } + + if (!(data = nvbios_pcirTp(bios, image->base, &ver, &hdr, &pcir))) + return false; + image->size = pcir.image_size; + image->type = pcir.image_type; + image->last = pcir.last; + + if (image->type != 0x70) { + if (!(data = nvbios_npdeTp(bios, image->base, &npde))) + return true; + image->size = npde.image_size; + image->last = npde.last; + } else { + image->last = true; + } + + return true; +} + +bool +nvbios_image(struct nvkm_bios *bios, int idx, struct nvbios_image *image) +{ + u32 imaged_addr = bios->imaged_addr; + memset(image, 0x00, sizeof(*image)); + bios->imaged_addr = 0; + do { + image->base += image->size; + if (image->last || !nvbios_imagen(bios, image)) { + bios->imaged_addr = imaged_addr; + return false; + } + } while(idx--); + bios->imaged_addr = imaged_addr; + return true; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c new file mode 100644 index 000000000..142079403 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c @@ -0,0 +1,2347 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define bioslog(lvl, fmt, args...) do { \ + nvkm_printk(init->subdev, lvl, info, "0x%08x[%c]: "fmt, \ + init->offset, init_exec(init) ? \ + '0' + (init->nested - 1) : ' ', ##args); \ +} while(0) +#define cont(fmt, args...) do { \ + if (init->subdev->debug >= NV_DBG_TRACE) \ + printk(fmt, ##args); \ +} while(0) +#define trace(fmt, args...) bioslog(TRACE, fmt, ##args) +#define warn(fmt, args...) bioslog(WARN, fmt, ##args) +#define error(fmt, args...) bioslog(ERROR, fmt, ##args) + +/****************************************************************************** + * init parser control flow helpers + *****************************************************************************/ + +static inline bool +init_exec(struct nvbios_init *init) +{ + return (init->execute == 1) || ((init->execute & 5) == 5); +} + +static inline void +init_exec_set(struct nvbios_init *init, bool exec) +{ + if (exec) init->execute &= 0xfd; + else init->execute |= 0x02; +} + +static inline void +init_exec_inv(struct nvbios_init *init) +{ + init->execute ^= 0x02; +} + +static inline void +init_exec_force(struct nvbios_init *init, bool exec) +{ + if (exec) init->execute |= 0x04; + else init->execute &= 0xfb; +} + +/****************************************************************************** + * init parser wrappers for normal register/i2c/whatever accessors + *****************************************************************************/ + +static inline int +init_or(struct nvbios_init *init) +{ + if (init_exec(init)) { + if (init->or >= 0) + return init->or; + error("script needs OR!!\n"); + } + return 0; +} + +static inline int +init_link(struct nvbios_init *init) +{ + if (init_exec(init)) { + if (init->link) + return init->link == 2; + error("script needs OR link\n"); + } + return 0; +} + +static inline int +init_head(struct nvbios_init *init) +{ + if (init_exec(init)) { + if (init->head >= 0) + return init->head; + error("script needs head\n"); + } + return 0; +} + +static u8 +init_conn(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + struct nvbios_connE connE; + u8 ver, hdr; + u32 conn; + + if (init_exec(init)) { + if (init->outp) { + conn = init->outp->connector; + conn = nvbios_connEp(bios, conn, &ver, &hdr, &connE); + if (conn) + return connE.type; + } + + error("script needs connector type\n"); + } + + return 0xff; +} + +static inline u32 +init_nvreg(struct nvbios_init *init, u32 reg) +{ + struct nvkm_devinit *devinit = init->subdev->device->devinit; + + /* C51 (at least) sometimes has the lower bits set which the VBIOS + * interprets to mean that access needs to go through certain IO + * ports instead. The NVIDIA binary driver has been seen to access + * these through the NV register address, so lets assume we can + * do the same + */ + reg &= ~0x00000003; + + /* GF8+ display scripts need register addresses mangled a bit to + * select a specific CRTC/OR + */ + if (init->subdev->device->card_type >= NV_50) { + if (reg & 0x80000000) { + reg += init_head(init) * 0x800; + reg &= ~0x80000000; + } + + if (reg & 0x40000000) { + reg += init_or(init) * 0x800; + reg &= ~0x40000000; + if (reg & 0x20000000) { + reg += init_link(init) * 0x80; + reg &= ~0x20000000; + } + } + } + + if (reg & ~0x00fffffc) + warn("unknown bits in register 0x%08x\n", reg); + + return nvkm_devinit_mmio(devinit, reg); +} + +static u32 +init_rd32(struct nvbios_init *init, u32 reg) +{ + struct nvkm_device *device = init->subdev->device; + reg = init_nvreg(init, reg); + if (reg != ~0 && init_exec(init)) + return nvkm_rd32(device, reg); + return 0x00000000; +} + +static void +init_wr32(struct nvbios_init *init, u32 reg, u32 val) +{ + struct nvkm_device *device = init->subdev->device; + reg = init_nvreg(init, reg); + if (reg != ~0 && init_exec(init)) + nvkm_wr32(device, reg, val); +} + +static u32 +init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val) +{ + struct nvkm_device *device = init->subdev->device; + reg = init_nvreg(init, reg); + if (reg != ~0 && init_exec(init)) { + u32 tmp = nvkm_rd32(device, reg); + nvkm_wr32(device, reg, (tmp & ~mask) | val); + return tmp; + } + return 0x00000000; +} + +static u8 +init_rdport(struct nvbios_init *init, u16 port) +{ + if (init_exec(init)) + return nvkm_rdport(init->subdev->device, init->head, port); + return 0x00; +} + +static void +init_wrport(struct nvbios_init *init, u16 port, u8 value) +{ + if (init_exec(init)) + nvkm_wrport(init->subdev->device, init->head, port, value); +} + +static u8 +init_rdvgai(struct nvbios_init *init, u16 port, u8 index) +{ + struct nvkm_subdev *subdev = init->subdev; + if (init_exec(init)) { + int head = init->head < 0 ? 0 : init->head; + return nvkm_rdvgai(subdev->device, head, port, index); + } + return 0x00; +} + +static void +init_wrvgai(struct nvbios_init *init, u16 port, u8 index, u8 value) +{ + struct nvkm_device *device = init->subdev->device; + + /* force head 0 for updates to cr44, it only exists on first head */ + if (device->card_type < NV_50) { + if (port == 0x03d4 && index == 0x44) + init->head = 0; + } + + if (init_exec(init)) { + int head = init->head < 0 ? 0 : init->head; + nvkm_wrvgai(device, head, port, index, value); + } + + /* select head 1 if cr44 write selected it */ + if (device->card_type < NV_50) { + if (port == 0x03d4 && index == 0x44 && value == 3) + init->head = 1; + } +} + +static struct i2c_adapter * +init_i2c(struct nvbios_init *init, int index) +{ + struct nvkm_i2c *i2c = init->subdev->device->i2c; + struct nvkm_i2c_bus *bus; + + if (index == 0xff) { + index = NVKM_I2C_BUS_PRI; + if (init->outp && init->outp->i2c_upper_default) + index = NVKM_I2C_BUS_SEC; + } else + if (index == 0x80) { + index = NVKM_I2C_BUS_PRI; + } else + if (index == 0x81) { + index = NVKM_I2C_BUS_SEC; + } + + bus = nvkm_i2c_bus_find(i2c, index); + return bus ? &bus->i2c : NULL; +} + +static int +init_rdi2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg) +{ + struct i2c_adapter *adap = init_i2c(init, index); + if (adap && init_exec(init)) + return nvkm_rdi2cr(adap, addr, reg); + return -ENODEV; +} + +static int +init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val) +{ + struct i2c_adapter *adap = init_i2c(init, index); + if (adap && init_exec(init)) + return nvkm_wri2cr(adap, addr, reg, val); + return -ENODEV; +} + +static struct nvkm_i2c_aux * +init_aux(struct nvbios_init *init) +{ + struct nvkm_i2c *i2c = init->subdev->device->i2c; + if (!init->outp) { + if (init_exec(init)) + error("script needs output for aux\n"); + return NULL; + } + return nvkm_i2c_aux_find(i2c, init->outp->i2c_index); +} + +static u8 +init_rdauxr(struct nvbios_init *init, u32 addr) +{ + struct nvkm_i2c_aux *aux = init_aux(init); + u8 data; + + if (aux && init_exec(init)) { + int ret = nvkm_rdaux(aux, addr, &data, 1); + if (ret == 0) + return data; + trace("auxch read failed with %d\n", ret); + } + + return 0x00; +} + +static int +init_wrauxr(struct nvbios_init *init, u32 addr, u8 data) +{ + struct nvkm_i2c_aux *aux = init_aux(init); + if (aux && init_exec(init)) { + int ret = nvkm_wraux(aux, addr, &data, 1); + if (ret) + trace("auxch write failed with %d\n", ret); + return ret; + } + return -ENODEV; +} + +static void +init_prog_pll(struct nvbios_init *init, u32 id, u32 freq) +{ + struct nvkm_devinit *devinit = init->subdev->device->devinit; + if (init_exec(init)) { + int ret = nvkm_devinit_pll_set(devinit, id, freq); + if (ret) + warn("failed to prog pll 0x%08x to %dkHz\n", id, freq); + } +} + +/****************************************************************************** + * parsing of bios structures that are required to execute init tables + *****************************************************************************/ + +static u16 +init_table(struct nvkm_bios *bios, u16 *len) +{ + struct bit_entry bit_I; + + if (!bit_entry(bios, 'I', &bit_I)) { + *len = bit_I.length; + return bit_I.offset; + } + + if (bmp_version(bios) >= 0x0510) { + *len = 14; + return bios->bmp_offset + 75; + } + + return 0x0000; +} + +static u16 +init_table_(struct nvbios_init *init, u16 offset, const char *name) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 len, data = init_table(bios, &len); + if (data) { + if (len >= offset + 2) { + data = nvbios_rd16(bios, data + offset); + if (data) + return data; + + warn("%s pointer invalid\n", name); + return 0x0000; + } + + warn("init data too short for %s pointer", name); + return 0x0000; + } + + warn("init data not found\n"); + return 0x0000; +} + +#define init_script_table(b) init_table_((b), 0x00, "script table") +#define init_macro_index_table(b) init_table_((b), 0x02, "macro index table") +#define init_macro_table(b) init_table_((b), 0x04, "macro table") +#define init_condition_table(b) init_table_((b), 0x06, "condition table") +#define init_io_condition_table(b) init_table_((b), 0x08, "io condition table") +#define init_io_flag_condition_table(b) init_table_((b), 0x0a, "io flag condition table") +#define init_function_table(b) init_table_((b), 0x0c, "function table") +#define init_xlat_table(b) init_table_((b), 0x10, "xlat table"); + +static u16 +init_script(struct nvkm_bios *bios, int index) +{ + struct nvbios_init init = { .subdev = &bios->subdev }; + u16 bmp_ver = bmp_version(bios), data; + + if (bmp_ver && bmp_ver < 0x0510) { + if (index > 1 || bmp_ver < 0x0100) + return 0x0000; + + data = bios->bmp_offset + (bmp_ver < 0x0200 ? 14 : 18); + return nvbios_rd16(bios, data + (index * 2)); + } + + data = init_script_table(&init); + if (data) + return nvbios_rd16(bios, data + (index * 2)); + + return 0x0000; +} + +static u16 +init_unknown_script(struct nvkm_bios *bios) +{ + u16 len, data = init_table(bios, &len); + if (data && len >= 16) + return nvbios_rd16(bios, data + 14); + return 0x0000; +} + +static u8 +init_ram_restrict_group_count(struct nvbios_init *init) +{ + return nvbios_ramcfg_count(init->subdev->device->bios); +} + +static u8 +init_ram_restrict(struct nvbios_init *init) +{ + /* This appears to be the behaviour of the VBIOS parser, and *is* + * important to cache the NV_PEXTDEV_BOOT0 on later chipsets to + * avoid fucking up the memory controller (somehow) by reading it + * on every INIT_RAM_RESTRICT_ZM_GROUP opcode. + * + * Preserving the non-caching behaviour on earlier chipsets just + * in case *not* re-reading the strap causes similar breakage. + */ + if (!init->ramcfg || init->subdev->device->bios->version.major < 0x70) + init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev); + return (init->ramcfg & 0x7fffffff); +} + +static u8 +init_xlat_(struct nvbios_init *init, u8 index, u8 offset) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 table = init_xlat_table(init); + if (table) { + u16 data = nvbios_rd16(bios, table + (index * 2)); + if (data) + return nvbios_rd08(bios, data + offset); + warn("xlat table pointer %d invalid\n", index); + } + return 0x00; +} + +/****************************************************************************** + * utility functions used by various init opcode handlers + *****************************************************************************/ + +static bool +init_condition_met(struct nvbios_init *init, u8 cond) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 table = init_condition_table(init); + if (table) { + u32 reg = nvbios_rd32(bios, table + (cond * 12) + 0); + u32 msk = nvbios_rd32(bios, table + (cond * 12) + 4); + u32 val = nvbios_rd32(bios, table + (cond * 12) + 8); + trace("\t[0x%02x] (R[0x%06x] & 0x%08x) == 0x%08x\n", + cond, reg, msk, val); + return (init_rd32(init, reg) & msk) == val; + } + return false; +} + +static bool +init_io_condition_met(struct nvbios_init *init, u8 cond) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 table = init_io_condition_table(init); + if (table) { + u16 port = nvbios_rd16(bios, table + (cond * 5) + 0); + u8 index = nvbios_rd08(bios, table + (cond * 5) + 2); + u8 mask = nvbios_rd08(bios, table + (cond * 5) + 3); + u8 value = nvbios_rd08(bios, table + (cond * 5) + 4); + trace("\t[0x%02x] (0x%04x[0x%02x] & 0x%02x) == 0x%02x\n", + cond, port, index, mask, value); + return (init_rdvgai(init, port, index) & mask) == value; + } + return false; +} + +static bool +init_io_flag_condition_met(struct nvbios_init *init, u8 cond) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 table = init_io_flag_condition_table(init); + if (table) { + u16 port = nvbios_rd16(bios, table + (cond * 9) + 0); + u8 index = nvbios_rd08(bios, table + (cond * 9) + 2); + u8 mask = nvbios_rd08(bios, table + (cond * 9) + 3); + u8 shift = nvbios_rd08(bios, table + (cond * 9) + 4); + u16 data = nvbios_rd16(bios, table + (cond * 9) + 5); + u8 dmask = nvbios_rd08(bios, table + (cond * 9) + 7); + u8 value = nvbios_rd08(bios, table + (cond * 9) + 8); + u8 ioval = (init_rdvgai(init, port, index) & mask) >> shift; + return (nvbios_rd08(bios, data + ioval) & dmask) == value; + } + return false; +} + +static inline u32 +init_shift(u32 data, u8 shift) +{ + if (shift < 0x80) + return data >> shift; + return data << (0x100 - shift); +} + +static u32 +init_tmds_reg(struct nvbios_init *init, u8 tmds) +{ + /* For mlv < 0x80, it is an index into a table of TMDS base addresses. + * For mlv == 0x80 use the "or" value of the dcb_entry indexed by + * CR58 for CR57 = 0 to index a table of offsets to the basic + * 0x6808b0 address. + * For mlv == 0x81 use the "or" value of the dcb_entry indexed by + * CR58 for CR57 = 0 to index a table of offsets to the basic + * 0x6808b0 address, and then flip the offset by 8. + */ + const int pramdac_offset[13] = { + 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 }; + const u32 pramdac_table[4] = { + 0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 }; + + if (tmds >= 0x80) { + if (init->outp) { + u32 dacoffset = pramdac_offset[init->outp->or]; + if (tmds == 0x81) + dacoffset ^= 8; + return 0x6808b0 + dacoffset; + } + + if (init_exec(init)) + error("tmds opcodes need dcb\n"); + } else { + if (tmds < ARRAY_SIZE(pramdac_table)) + return pramdac_table[tmds]; + + error("tmds selector 0x%02x unknown\n", tmds); + } + + return 0; +} + +/****************************************************************************** + * init opcode handlers + *****************************************************************************/ + +/** + * init_reserved - stub for various unknown/unused single-byte opcodes + * + */ +static void +init_reserved(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 opcode = nvbios_rd08(bios, init->offset); + u8 length, i; + + switch (opcode) { + case 0xaa: + length = 4; + break; + default: + length = 1; + break; + } + + trace("RESERVED 0x%02x\t", opcode); + for (i = 1; i < length; i++) + cont(" 0x%02x", nvbios_rd08(bios, init->offset + i)); + cont("\n"); + init->offset += length; +} + +/** + * INIT_DONE - opcode 0x71 + * + */ +static void +init_done(struct nvbios_init *init) +{ + trace("DONE\n"); + init->offset = 0x0000; +} + +/** + * INIT_IO_RESTRICT_PROG - opcode 0x32 + * + */ +static void +init_io_restrict_prog(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 port = nvbios_rd16(bios, init->offset + 1); + u8 index = nvbios_rd08(bios, init->offset + 3); + u8 mask = nvbios_rd08(bios, init->offset + 4); + u8 shift = nvbios_rd08(bios, init->offset + 5); + u8 count = nvbios_rd08(bios, init->offset + 6); + u32 reg = nvbios_rd32(bios, init->offset + 7); + u8 conf, i; + + trace("IO_RESTRICT_PROG\tR[0x%06x] = " + "((0x%04x[0x%02x] & 0x%02x) >> %d) [{\n", + reg, port, index, mask, shift); + init->offset += 11; + + conf = (init_rdvgai(init, port, index) & mask) >> shift; + for (i = 0; i < count; i++) { + u32 data = nvbios_rd32(bios, init->offset); + + if (i == conf) { + trace("\t0x%08x *\n", data); + init_wr32(init, reg, data); + } else { + trace("\t0x%08x\n", data); + } + + init->offset += 4; + } + trace("}]\n"); +} + +/** + * INIT_REPEAT - opcode 0x33 + * + */ +static void +init_repeat(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 count = nvbios_rd08(bios, init->offset + 1); + u16 repeat = init->repeat; + + trace("REPEAT\t0x%02x\n", count); + init->offset += 2; + + init->repeat = init->offset; + init->repend = init->offset; + while (count--) { + init->offset = init->repeat; + nvbios_exec(init); + if (count) + trace("REPEAT\t0x%02x\n", count); + } + init->offset = init->repend; + init->repeat = repeat; +} + +/** + * INIT_IO_RESTRICT_PLL - opcode 0x34 + * + */ +static void +init_io_restrict_pll(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 port = nvbios_rd16(bios, init->offset + 1); + u8 index = nvbios_rd08(bios, init->offset + 3); + u8 mask = nvbios_rd08(bios, init->offset + 4); + u8 shift = nvbios_rd08(bios, init->offset + 5); + s8 iofc = nvbios_rd08(bios, init->offset + 6); + u8 count = nvbios_rd08(bios, init->offset + 7); + u32 reg = nvbios_rd32(bios, init->offset + 8); + u8 conf, i; + + trace("IO_RESTRICT_PLL\tR[0x%06x] =PLL= " + "((0x%04x[0x%02x] & 0x%02x) >> 0x%02x) IOFCOND 0x%02x [{\n", + reg, port, index, mask, shift, iofc); + init->offset += 12; + + conf = (init_rdvgai(init, port, index) & mask) >> shift; + for (i = 0; i < count; i++) { + u32 freq = nvbios_rd16(bios, init->offset) * 10; + + if (i == conf) { + trace("\t%dkHz *\n", freq); + if (iofc > 0 && init_io_flag_condition_met(init, iofc)) + freq *= 2; + init_prog_pll(init, reg, freq); + } else { + trace("\t%dkHz\n", freq); + } + + init->offset += 2; + } + trace("}]\n"); +} + +/** + * INIT_END_REPEAT - opcode 0x36 + * + */ +static void +init_end_repeat(struct nvbios_init *init) +{ + trace("END_REPEAT\n"); + init->offset += 1; + + if (init->repeat) { + init->repend = init->offset; + init->offset = 0; + } +} + +/** + * INIT_COPY - opcode 0x37 + * + */ +static void +init_copy(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u8 shift = nvbios_rd08(bios, init->offset + 5); + u8 smask = nvbios_rd08(bios, init->offset + 6); + u16 port = nvbios_rd16(bios, init->offset + 7); + u8 index = nvbios_rd08(bios, init->offset + 9); + u8 mask = nvbios_rd08(bios, init->offset + 10); + u8 data; + + trace("COPY\t0x%04x[0x%02x] &= 0x%02x |= " + "((R[0x%06x] %s 0x%02x) & 0x%02x)\n", + port, index, mask, reg, (shift & 0x80) ? "<<" : ">>", + (shift & 0x80) ? (0x100 - shift) : shift, smask); + init->offset += 11; + + data = init_rdvgai(init, port, index) & mask; + data |= init_shift(init_rd32(init, reg), shift) & smask; + init_wrvgai(init, port, index, data); +} + +/** + * INIT_NOT - opcode 0x38 + * + */ +static void +init_not(struct nvbios_init *init) +{ + trace("NOT\n"); + init->offset += 1; + init_exec_inv(init); +} + +/** + * INIT_IO_FLAG_CONDITION - opcode 0x39 + * + */ +static void +init_io_flag_condition(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 cond = nvbios_rd08(bios, init->offset + 1); + + trace("IO_FLAG_CONDITION\t0x%02x\n", cond); + init->offset += 2; + + if (!init_io_flag_condition_met(init, cond)) + init_exec_set(init, false); +} + +/** + * INIT_GENERIC_CONDITION - opcode 0x3a + * + */ +static void +init_generic_condition(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + struct nvbios_dpout info; + u8 cond = nvbios_rd08(bios, init->offset + 1); + u8 size = nvbios_rd08(bios, init->offset + 2); + u8 ver, hdr, cnt, len; + u16 data; + + trace("GENERIC_CONDITION\t0x%02x 0x%02x\n", cond, size); + init->offset += 3; + + switch (cond) { + case 0: /* CONDITION_ID_INT_DP. */ + if (init_conn(init) != DCB_CONNECTOR_eDP) + init_exec_set(init, false); + break; + case 1: /* CONDITION_ID_USE_SPPLL0. */ + case 2: /* CONDITION_ID_USE_SPPLL1. */ + if ( init->outp && + (data = nvbios_dpout_match(bios, DCB_OUTPUT_DP, + (init->outp->or << 0) | + (init->outp->sorconf.link << 6), + &ver, &hdr, &cnt, &len, &info))) + { + if (!(info.flags & cond)) + init_exec_set(init, false); + break; + } + + if (init_exec(init)) + warn("script needs dp output table data\n"); + break; + case 5: /* CONDITION_ID_ASSR_SUPPORT. */ + if (!(init_rdauxr(init, 0x0d) & 1)) + init_exec_set(init, false); + break; + case 7: /* CONDITION_ID_NO_PANEL_SEQ_DELAYS. */ + init_exec_set(init, false); + break; + default: + warn("INIT_GENERIC_CONDITION: unknown 0x%02x\n", cond); + init->offset += size; + break; + } +} + +/** + * INIT_IO_MASK_OR - opcode 0x3b + * + */ +static void +init_io_mask_or(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u8 or = init_or(init); + u8 data; + + trace("IO_MASK_OR\t0x03d4[0x%02x] &= ~(1 << 0x%02x)\n", index, or); + init->offset += 2; + + data = init_rdvgai(init, 0x03d4, index); + init_wrvgai(init, 0x03d4, index, data &= ~(1 << or)); +} + +/** + * INIT_IO_OR - opcode 0x3c + * + */ +static void +init_io_or(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u8 or = init_or(init); + u8 data; + + trace("IO_OR\t0x03d4[0x%02x] |= (1 << 0x%02x)\n", index, or); + init->offset += 2; + + data = init_rdvgai(init, 0x03d4, index); + init_wrvgai(init, 0x03d4, index, data | (1 << or)); +} + +/** + * INIT_ANDN_REG - opcode 0x47 + * + */ +static void +init_andn_reg(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u32 mask = nvbios_rd32(bios, init->offset + 5); + + trace("ANDN_REG\tR[0x%06x] &= ~0x%08x\n", reg, mask); + init->offset += 9; + + init_mask(init, reg, mask, 0); +} + +/** + * INIT_OR_REG - opcode 0x48 + * + */ +static void +init_or_reg(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u32 mask = nvbios_rd32(bios, init->offset + 5); + + trace("OR_REG\tR[0x%06x] |= 0x%08x\n", reg, mask); + init->offset += 9; + + init_mask(init, reg, 0, mask); +} + +/** + * INIT_INDEX_ADDRESS_LATCHED - opcode 0x49 + * + */ +static void +init_idx_addr_latched(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 creg = nvbios_rd32(bios, init->offset + 1); + u32 dreg = nvbios_rd32(bios, init->offset + 5); + u32 mask = nvbios_rd32(bios, init->offset + 9); + u32 data = nvbios_rd32(bios, init->offset + 13); + u8 count = nvbios_rd08(bios, init->offset + 17); + + trace("INDEX_ADDRESS_LATCHED\tR[0x%06x] : R[0x%06x]\n", creg, dreg); + trace("\tCTRL &= 0x%08x |= 0x%08x\n", mask, data); + init->offset += 18; + + while (count--) { + u8 iaddr = nvbios_rd08(bios, init->offset + 0); + u8 idata = nvbios_rd08(bios, init->offset + 1); + + trace("\t[0x%02x] = 0x%02x\n", iaddr, idata); + init->offset += 2; + + init_wr32(init, dreg, idata); + init_mask(init, creg, ~mask, data | iaddr); + } +} + +/** + * INIT_IO_RESTRICT_PLL2 - opcode 0x4a + * + */ +static void +init_io_restrict_pll2(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 port = nvbios_rd16(bios, init->offset + 1); + u8 index = nvbios_rd08(bios, init->offset + 3); + u8 mask = nvbios_rd08(bios, init->offset + 4); + u8 shift = nvbios_rd08(bios, init->offset + 5); + u8 count = nvbios_rd08(bios, init->offset + 6); + u32 reg = nvbios_rd32(bios, init->offset + 7); + u8 conf, i; + + trace("IO_RESTRICT_PLL2\t" + "R[0x%06x] =PLL= ((0x%04x[0x%02x] & 0x%02x) >> 0x%02x) [{\n", + reg, port, index, mask, shift); + init->offset += 11; + + conf = (init_rdvgai(init, port, index) & mask) >> shift; + for (i = 0; i < count; i++) { + u32 freq = nvbios_rd32(bios, init->offset); + if (i == conf) { + trace("\t%dkHz *\n", freq); + init_prog_pll(init, reg, freq); + } else { + trace("\t%dkHz\n", freq); + } + init->offset += 4; + } + trace("}]\n"); +} + +/** + * INIT_PLL2 - opcode 0x4b + * + */ +static void +init_pll2(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u32 freq = nvbios_rd32(bios, init->offset + 5); + + trace("PLL2\tR[0x%06x] =PLL= %dkHz\n", reg, freq); + init->offset += 9; + + init_prog_pll(init, reg, freq); +} + +/** + * INIT_I2C_BYTE - opcode 0x4c + * + */ +static void +init_i2c_byte(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; + u8 count = nvbios_rd08(bios, init->offset + 3); + + trace("I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr); + init->offset += 4; + + while (count--) { + u8 reg = nvbios_rd08(bios, init->offset + 0); + u8 mask = nvbios_rd08(bios, init->offset + 1); + u8 data = nvbios_rd08(bios, init->offset + 2); + int val; + + trace("\t[0x%02x] &= 0x%02x |= 0x%02x\n", reg, mask, data); + init->offset += 3; + + val = init_rdi2cr(init, index, addr, reg); + if (val < 0) + continue; + init_wri2cr(init, index, addr, reg, (val & mask) | data); + } +} + +/** + * INIT_ZM_I2C_BYTE - opcode 0x4d + * + */ +static void +init_zm_i2c_byte(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; + u8 count = nvbios_rd08(bios, init->offset + 3); + + trace("ZM_I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr); + init->offset += 4; + + while (count--) { + u8 reg = nvbios_rd08(bios, init->offset + 0); + u8 data = nvbios_rd08(bios, init->offset + 1); + + trace("\t[0x%02x] = 0x%02x\n", reg, data); + init->offset += 2; + + init_wri2cr(init, index, addr, reg, data); + } +} + +/** + * INIT_ZM_I2C - opcode 0x4e + * + */ +static void +init_zm_i2c(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; + u8 count = nvbios_rd08(bios, init->offset + 3); + u8 data[256], i; + + trace("ZM_I2C\tI2C[0x%02x][0x%02x]\n", index, addr); + init->offset += 4; + + for (i = 0; i < count; i++) { + data[i] = nvbios_rd08(bios, init->offset); + trace("\t0x%02x\n", data[i]); + init->offset++; + } + + if (init_exec(init)) { + struct i2c_adapter *adap = init_i2c(init, index); + struct i2c_msg msg = { + .addr = addr, .flags = 0, .len = count, .buf = data, + }; + int ret; + + if (adap && (ret = i2c_transfer(adap, &msg, 1)) != 1) + warn("i2c wr failed, %d\n", ret); + } +} + +/** + * INIT_TMDS - opcode 0x4f + * + */ +static void +init_tmds(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 tmds = nvbios_rd08(bios, init->offset + 1); + u8 addr = nvbios_rd08(bios, init->offset + 2); + u8 mask = nvbios_rd08(bios, init->offset + 3); + u8 data = nvbios_rd08(bios, init->offset + 4); + u32 reg = init_tmds_reg(init, tmds); + + trace("TMDS\tT[0x%02x][0x%02x] &= 0x%02x |= 0x%02x\n", + tmds, addr, mask, data); + init->offset += 5; + + if (reg == 0) + return; + + init_wr32(init, reg + 0, addr | 0x00010000); + init_wr32(init, reg + 4, data | (init_rd32(init, reg + 4) & mask)); + init_wr32(init, reg + 0, addr); +} + +/** + * INIT_ZM_TMDS_GROUP - opcode 0x50 + * + */ +static void +init_zm_tmds_group(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 tmds = nvbios_rd08(bios, init->offset + 1); + u8 count = nvbios_rd08(bios, init->offset + 2); + u32 reg = init_tmds_reg(init, tmds); + + trace("TMDS_ZM_GROUP\tT[0x%02x]\n", tmds); + init->offset += 3; + + while (count--) { + u8 addr = nvbios_rd08(bios, init->offset + 0); + u8 data = nvbios_rd08(bios, init->offset + 1); + + trace("\t[0x%02x] = 0x%02x\n", addr, data); + init->offset += 2; + + init_wr32(init, reg + 4, data); + init_wr32(init, reg + 0, addr); + } +} + +/** + * INIT_CR_INDEX_ADDRESS_LATCHED - opcode 0x51 + * + */ +static void +init_cr_idx_adr_latch(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 addr0 = nvbios_rd08(bios, init->offset + 1); + u8 addr1 = nvbios_rd08(bios, init->offset + 2); + u8 base = nvbios_rd08(bios, init->offset + 3); + u8 count = nvbios_rd08(bios, init->offset + 4); + u8 save0; + + trace("CR_INDEX_ADDR C[%02x] C[%02x]\n", addr0, addr1); + init->offset += 5; + + save0 = init_rdvgai(init, 0x03d4, addr0); + while (count--) { + u8 data = nvbios_rd08(bios, init->offset); + + trace("\t\t[0x%02x] = 0x%02x\n", base, data); + init->offset += 1; + + init_wrvgai(init, 0x03d4, addr0, base++); + init_wrvgai(init, 0x03d4, addr1, data); + } + init_wrvgai(init, 0x03d4, addr0, save0); +} + +/** + * INIT_CR - opcode 0x52 + * + */ +static void +init_cr(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 addr = nvbios_rd08(bios, init->offset + 1); + u8 mask = nvbios_rd08(bios, init->offset + 2); + u8 data = nvbios_rd08(bios, init->offset + 3); + u8 val; + + trace("CR\t\tC[0x%02x] &= 0x%02x |= 0x%02x\n", addr, mask, data); + init->offset += 4; + + val = init_rdvgai(init, 0x03d4, addr) & mask; + init_wrvgai(init, 0x03d4, addr, val | data); +} + +/** + * INIT_ZM_CR - opcode 0x53 + * + */ +static void +init_zm_cr(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 addr = nvbios_rd08(bios, init->offset + 1); + u8 data = nvbios_rd08(bios, init->offset + 2); + + trace("ZM_CR\tC[0x%02x] = 0x%02x\n", addr, data); + init->offset += 3; + + init_wrvgai(init, 0x03d4, addr, data); +} + +/** + * INIT_ZM_CR_GROUP - opcode 0x54 + * + */ +static void +init_zm_cr_group(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 count = nvbios_rd08(bios, init->offset + 1); + + trace("ZM_CR_GROUP\n"); + init->offset += 2; + + while (count--) { + u8 addr = nvbios_rd08(bios, init->offset + 0); + u8 data = nvbios_rd08(bios, init->offset + 1); + + trace("\t\tC[0x%02x] = 0x%02x\n", addr, data); + init->offset += 2; + + init_wrvgai(init, 0x03d4, addr, data); + } +} + +/** + * INIT_CONDITION_TIME - opcode 0x56 + * + */ +static void +init_condition_time(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 cond = nvbios_rd08(bios, init->offset + 1); + u8 retry = nvbios_rd08(bios, init->offset + 2); + u8 wait = min((u16)retry * 50, 100); + + trace("CONDITION_TIME\t0x%02x 0x%02x\n", cond, retry); + init->offset += 3; + + if (!init_exec(init)) + return; + + while (wait--) { + if (init_condition_met(init, cond)) + return; + mdelay(20); + } + + init_exec_set(init, false); +} + +/** + * INIT_LTIME - opcode 0x57 + * + */ +static void +init_ltime(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 msec = nvbios_rd16(bios, init->offset + 1); + + trace("LTIME\t0x%04x\n", msec); + init->offset += 3; + + if (init_exec(init)) + mdelay(msec); +} + +/** + * INIT_ZM_REG_SEQUENCE - opcode 0x58 + * + */ +static void +init_zm_reg_sequence(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 base = nvbios_rd32(bios, init->offset + 1); + u8 count = nvbios_rd08(bios, init->offset + 5); + + trace("ZM_REG_SEQUENCE\t0x%02x\n", count); + init->offset += 6; + + while (count--) { + u32 data = nvbios_rd32(bios, init->offset); + + trace("\t\tR[0x%06x] = 0x%08x\n", base, data); + init->offset += 4; + + init_wr32(init, base, data); + base += 4; + } +} + +/** + * INIT_PLL_INDIRECT - opcode 0x59 + * + */ +static void +init_pll_indirect(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u16 addr = nvbios_rd16(bios, init->offset + 5); + u32 freq = (u32)nvbios_rd16(bios, addr) * 1000; + + trace("PLL_INDIRECT\tR[0x%06x] =PLL= VBIOS[%04x] = %dkHz\n", + reg, addr, freq); + init->offset += 7; + + init_prog_pll(init, reg, freq); +} + +/** + * INIT_ZM_REG_INDIRECT - opcode 0x5a + * + */ +static void +init_zm_reg_indirect(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u16 addr = nvbios_rd16(bios, init->offset + 5); + u32 data = nvbios_rd32(bios, addr); + + trace("ZM_REG_INDIRECT\tR[0x%06x] = VBIOS[0x%04x] = 0x%08x\n", + reg, addr, data); + init->offset += 7; + + init_wr32(init, addr, data); +} + +/** + * INIT_SUB_DIRECT - opcode 0x5b + * + */ +static void +init_sub_direct(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 addr = nvbios_rd16(bios, init->offset + 1); + u16 save; + + trace("SUB_DIRECT\t0x%04x\n", addr); + + if (init_exec(init)) { + save = init->offset; + init->offset = addr; + if (nvbios_exec(init)) { + error("error parsing sub-table\n"); + return; + } + init->offset = save; + } + + init->offset += 3; +} + +/** + * INIT_JUMP - opcode 0x5c + * + */ +static void +init_jump(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 offset = nvbios_rd16(bios, init->offset + 1); + + trace("JUMP\t0x%04x\n", offset); + + if (init_exec(init)) + init->offset = offset; + else + init->offset += 3; +} + +/** + * INIT_I2C_IF - opcode 0x5e + * + */ +static void +init_i2c_if(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u8 addr = nvbios_rd08(bios, init->offset + 2); + u8 reg = nvbios_rd08(bios, init->offset + 3); + u8 mask = nvbios_rd08(bios, init->offset + 4); + u8 data = nvbios_rd08(bios, init->offset + 5); + u8 value; + + trace("I2C_IF\tI2C[0x%02x][0x%02x][0x%02x] & 0x%02x == 0x%02x\n", + index, addr, reg, mask, data); + init->offset += 6; + init_exec_force(init, true); + + value = init_rdi2cr(init, index, addr, reg); + if ((value & mask) != data) + init_exec_set(init, false); + + init_exec_force(init, false); +} + +/** + * INIT_COPY_NV_REG - opcode 0x5f + * + */ +static void +init_copy_nv_reg(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 sreg = nvbios_rd32(bios, init->offset + 1); + u8 shift = nvbios_rd08(bios, init->offset + 5); + u32 smask = nvbios_rd32(bios, init->offset + 6); + u32 sxor = nvbios_rd32(bios, init->offset + 10); + u32 dreg = nvbios_rd32(bios, init->offset + 14); + u32 dmask = nvbios_rd32(bios, init->offset + 18); + u32 data; + + trace("COPY_NV_REG\tR[0x%06x] &= 0x%08x |= " + "((R[0x%06x] %s 0x%02x) & 0x%08x ^ 0x%08x)\n", + dreg, dmask, sreg, (shift & 0x80) ? "<<" : ">>", + (shift & 0x80) ? (0x100 - shift) : shift, smask, sxor); + init->offset += 22; + + data = init_shift(init_rd32(init, sreg), shift); + init_mask(init, dreg, ~dmask, (data & smask) ^ sxor); +} + +/** + * INIT_ZM_INDEX_IO - opcode 0x62 + * + */ +static void +init_zm_index_io(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 port = nvbios_rd16(bios, init->offset + 1); + u8 index = nvbios_rd08(bios, init->offset + 3); + u8 data = nvbios_rd08(bios, init->offset + 4); + + trace("ZM_INDEX_IO\tI[0x%04x][0x%02x] = 0x%02x\n", port, index, data); + init->offset += 5; + + init_wrvgai(init, port, index, data); +} + +/** + * INIT_COMPUTE_MEM - opcode 0x63 + * + */ +static void +init_compute_mem(struct nvbios_init *init) +{ + struct nvkm_devinit *devinit = init->subdev->device->devinit; + + trace("COMPUTE_MEM\n"); + init->offset += 1; + + init_exec_force(init, true); + if (init_exec(init)) + nvkm_devinit_meminit(devinit); + init_exec_force(init, false); +} + +/** + * INIT_RESET - opcode 0x65 + * + */ +static void +init_reset(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u32 data1 = nvbios_rd32(bios, init->offset + 5); + u32 data2 = nvbios_rd32(bios, init->offset + 9); + u32 savepci19; + + trace("RESET\tR[0x%08x] = 0x%08x, 0x%08x", reg, data1, data2); + init->offset += 13; + init_exec_force(init, true); + + savepci19 = init_mask(init, 0x00184c, 0x00000f00, 0x00000000); + init_wr32(init, reg, data1); + udelay(10); + init_wr32(init, reg, data2); + init_wr32(init, 0x00184c, savepci19); + init_mask(init, 0x001850, 0x00000001, 0x00000000); + + init_exec_force(init, false); +} + +/** + * INIT_CONFIGURE_MEM - opcode 0x66 + * + */ +static u16 +init_configure_mem_clk(struct nvbios_init *init) +{ + u16 mdata = bmp_mem_init_table(init->subdev->device->bios); + if (mdata) + mdata += (init_rdvgai(init, 0x03d4, 0x3c) >> 4) * 66; + return mdata; +} + +static void +init_configure_mem(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 mdata, sdata; + u32 addr, data; + + trace("CONFIGURE_MEM\n"); + init->offset += 1; + + if (bios->version.major > 2) { + init_done(init); + return; + } + init_exec_force(init, true); + + mdata = init_configure_mem_clk(init); + sdata = bmp_sdr_seq_table(bios); + if (nvbios_rd08(bios, mdata) & 0x01) + sdata = bmp_ddr_seq_table(bios); + mdata += 6; /* skip to data */ + + data = init_rdvgai(init, 0x03c4, 0x01); + init_wrvgai(init, 0x03c4, 0x01, data | 0x20); + + for (; (addr = nvbios_rd32(bios, sdata)) != 0xffffffff; sdata += 4) { + switch (addr) { + case 0x10021c: /* CKE_NORMAL */ + case 0x1002d0: /* CMD_REFRESH */ + case 0x1002d4: /* CMD_PRECHARGE */ + data = 0x00000001; + break; + default: + data = nvbios_rd32(bios, mdata); + mdata += 4; + if (data == 0xffffffff) + continue; + break; + } + + init_wr32(init, addr, data); + } + + init_exec_force(init, false); +} + +/** + * INIT_CONFIGURE_CLK - opcode 0x67 + * + */ +static void +init_configure_clk(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 mdata, clock; + + trace("CONFIGURE_CLK\n"); + init->offset += 1; + + if (bios->version.major > 2) { + init_done(init); + return; + } + init_exec_force(init, true); + + mdata = init_configure_mem_clk(init); + + /* NVPLL */ + clock = nvbios_rd16(bios, mdata + 4) * 10; + init_prog_pll(init, 0x680500, clock); + + /* MPLL */ + clock = nvbios_rd16(bios, mdata + 2) * 10; + if (nvbios_rd08(bios, mdata) & 0x01) + clock *= 2; + init_prog_pll(init, 0x680504, clock); + + init_exec_force(init, false); +} + +/** + * INIT_CONFIGURE_PREINIT - opcode 0x68 + * + */ +static void +init_configure_preinit(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 strap; + + trace("CONFIGURE_PREINIT\n"); + init->offset += 1; + + if (bios->version.major > 2) { + init_done(init); + return; + } + init_exec_force(init, true); + + strap = init_rd32(init, 0x101000); + strap = ((strap << 2) & 0xf0) | ((strap & 0x40) >> 6); + init_wrvgai(init, 0x03d4, 0x3c, strap); + + init_exec_force(init, false); +} + +/** + * INIT_IO - opcode 0x69 + * + */ +static void +init_io(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 port = nvbios_rd16(bios, init->offset + 1); + u8 mask = nvbios_rd16(bios, init->offset + 3); + u8 data = nvbios_rd16(bios, init->offset + 4); + u8 value; + + trace("IO\t\tI[0x%04x] &= 0x%02x |= 0x%02x\n", port, mask, data); + init->offset += 5; + + /* ummm.. yes.. should really figure out wtf this is and why it's + * needed some day.. it's almost certainly wrong, but, it also + * somehow makes things work... + */ + if (bios->subdev.device->card_type >= NV_50 && + port == 0x03c3 && data == 0x01) { + init_mask(init, 0x614100, 0xf0800000, 0x00800000); + init_mask(init, 0x00e18c, 0x00020000, 0x00020000); + init_mask(init, 0x614900, 0xf0800000, 0x00800000); + init_mask(init, 0x000200, 0x40000000, 0x00000000); + mdelay(10); + init_mask(init, 0x00e18c, 0x00020000, 0x00000000); + init_mask(init, 0x000200, 0x40000000, 0x40000000); + init_wr32(init, 0x614100, 0x00800018); + init_wr32(init, 0x614900, 0x00800018); + mdelay(10); + init_wr32(init, 0x614100, 0x10000018); + init_wr32(init, 0x614900, 0x10000018); + } + + value = init_rdport(init, port) & mask; + init_wrport(init, port, data | value); +} + +/** + * INIT_SUB - opcode 0x6b + * + */ +static void +init_sub(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u16 addr, save; + + trace("SUB\t0x%02x\n", index); + + addr = init_script(bios, index); + if (addr && init_exec(init)) { + save = init->offset; + init->offset = addr; + if (nvbios_exec(init)) { + error("error parsing sub-table\n"); + return; + } + init->offset = save; + } + + init->offset += 2; +} + +/** + * INIT_RAM_CONDITION - opcode 0x6d + * + */ +static void +init_ram_condition(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 mask = nvbios_rd08(bios, init->offset + 1); + u8 value = nvbios_rd08(bios, init->offset + 2); + + trace("RAM_CONDITION\t" + "(R[0x100000] & 0x%02x) == 0x%02x\n", mask, value); + init->offset += 3; + + if ((init_rd32(init, 0x100000) & mask) != value) + init_exec_set(init, false); +} + +/** + * INIT_NV_REG - opcode 0x6e + * + */ +static void +init_nv_reg(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u32 mask = nvbios_rd32(bios, init->offset + 5); + u32 data = nvbios_rd32(bios, init->offset + 9); + + trace("NV_REG\tR[0x%06x] &= 0x%08x |= 0x%08x\n", reg, mask, data); + init->offset += 13; + + init_mask(init, reg, ~mask, data); +} + +/** + * INIT_MACRO - opcode 0x6f + * + */ +static void +init_macro(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 macro = nvbios_rd08(bios, init->offset + 1); + u16 table; + + trace("MACRO\t0x%02x\n", macro); + + table = init_macro_table(init); + if (table) { + u32 addr = nvbios_rd32(bios, table + (macro * 8) + 0); + u32 data = nvbios_rd32(bios, table + (macro * 8) + 4); + trace("\t\tR[0x%06x] = 0x%08x\n", addr, data); + init_wr32(init, addr, data); + } + + init->offset += 2; +} + +/** + * INIT_RESUME - opcode 0x72 + * + */ +static void +init_resume(struct nvbios_init *init) +{ + trace("RESUME\n"); + init->offset += 1; + init_exec_set(init, true); +} + +/** + * INIT_STRAP_CONDITION - opcode 0x73 + * + */ +static void +init_strap_condition(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 mask = nvbios_rd32(bios, init->offset + 1); + u32 value = nvbios_rd32(bios, init->offset + 5); + + trace("STRAP_CONDITION\t(R[0x101000] & 0x%08x) == 0x%08x\n", mask, value); + init->offset += 9; + + if ((init_rd32(init, 0x101000) & mask) != value) + init_exec_set(init, false); +} + +/** + * INIT_TIME - opcode 0x74 + * + */ +static void +init_time(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 usec = nvbios_rd16(bios, init->offset + 1); + + trace("TIME\t0x%04x\n", usec); + init->offset += 3; + + if (init_exec(init)) { + if (usec < 1000) + udelay(usec); + else + mdelay((usec + 900) / 1000); + } +} + +/** + * INIT_CONDITION - opcode 0x75 + * + */ +static void +init_condition(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 cond = nvbios_rd08(bios, init->offset + 1); + + trace("CONDITION\t0x%02x\n", cond); + init->offset += 2; + + if (!init_condition_met(init, cond)) + init_exec_set(init, false); +} + +/** + * INIT_IO_CONDITION - opcode 0x76 + * + */ +static void +init_io_condition(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 cond = nvbios_rd08(bios, init->offset + 1); + + trace("IO_CONDITION\t0x%02x\n", cond); + init->offset += 2; + + if (!init_io_condition_met(init, cond)) + init_exec_set(init, false); +} + +/** + * INIT_ZM_REG16 - opcode 0x77 + * + */ +static void +init_zm_reg16(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 addr = nvbios_rd32(bios, init->offset + 1); + u16 data = nvbios_rd16(bios, init->offset + 5); + + trace("ZM_REG\tR[0x%06x] = 0x%04x\n", addr, data); + init->offset += 7; + + init_wr32(init, addr, data); +} + +/** + * INIT_INDEX_IO - opcode 0x78 + * + */ +static void +init_index_io(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u16 port = nvbios_rd16(bios, init->offset + 1); + u8 index = nvbios_rd16(bios, init->offset + 3); + u8 mask = nvbios_rd08(bios, init->offset + 4); + u8 data = nvbios_rd08(bios, init->offset + 5); + u8 value; + + trace("INDEX_IO\tI[0x%04x][0x%02x] &= 0x%02x |= 0x%02x\n", + port, index, mask, data); + init->offset += 6; + + value = init_rdvgai(init, port, index) & mask; + init_wrvgai(init, port, index, data | value); +} + +/** + * INIT_PLL - opcode 0x79 + * + */ +static void +init_pll(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 reg = nvbios_rd32(bios, init->offset + 1); + u32 freq = nvbios_rd16(bios, init->offset + 5) * 10; + + trace("PLL\tR[0x%06x] =PLL= %dkHz\n", reg, freq); + init->offset += 7; + + init_prog_pll(init, reg, freq); +} + +/** + * INIT_ZM_REG - opcode 0x7a + * + */ +static void +init_zm_reg(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 addr = nvbios_rd32(bios, init->offset + 1); + u32 data = nvbios_rd32(bios, init->offset + 5); + + trace("ZM_REG\tR[0x%06x] = 0x%08x\n", addr, data); + init->offset += 9; + + if (addr == 0x000200) + data |= 0x00000001; + + init_wr32(init, addr, data); +} + +/** + * INIT_RAM_RESTRICT_PLL - opcde 0x87 + * + */ +static void +init_ram_restrict_pll(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 type = nvbios_rd08(bios, init->offset + 1); + u8 count = init_ram_restrict_group_count(init); + u8 strap = init_ram_restrict(init); + u8 cconf; + + trace("RAM_RESTRICT_PLL\t0x%02x\n", type); + init->offset += 2; + + for (cconf = 0; cconf < count; cconf++) { + u32 freq = nvbios_rd32(bios, init->offset); + + if (cconf == strap) { + trace("%dkHz *\n", freq); + init_prog_pll(init, type, freq); + } else { + trace("%dkHz\n", freq); + } + + init->offset += 4; + } +} + +/** + * INIT_RESET_BEGUN - opcode 0x8c + * + */ +static void +init_reset_begun(struct nvbios_init *init) +{ + trace("RESET_BEGUN\n"); + init->offset += 1; +} + +/** + * INIT_RESET_END - opcode 0x8d + * + */ +static void +init_reset_end(struct nvbios_init *init) +{ + trace("RESET_END\n"); + init->offset += 1; +} + +/** + * INIT_GPIO - opcode 0x8e + * + */ +static void +init_gpio(struct nvbios_init *init) +{ + struct nvkm_gpio *gpio = init->subdev->device->gpio; + + trace("GPIO\n"); + init->offset += 1; + + if (init_exec(init)) + nvkm_gpio_reset(gpio, DCB_GPIO_UNUSED); +} + +/** + * INIT_RAM_RESTRICT_ZM_GROUP - opcode 0x8f + * + */ +static void +init_ram_restrict_zm_reg_group(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 addr = nvbios_rd32(bios, init->offset + 1); + u8 incr = nvbios_rd08(bios, init->offset + 5); + u8 num = nvbios_rd08(bios, init->offset + 6); + u8 count = init_ram_restrict_group_count(init); + u8 index = init_ram_restrict(init); + u8 i, j; + + trace("RAM_RESTRICT_ZM_REG_GROUP\t" + "R[0x%08x] 0x%02x 0x%02x\n", addr, incr, num); + init->offset += 7; + + for (i = 0; i < num; i++) { + trace("\tR[0x%06x] = {\n", addr); + for (j = 0; j < count; j++) { + u32 data = nvbios_rd32(bios, init->offset); + + if (j == index) { + trace("\t\t0x%08x *\n", data); + init_wr32(init, addr, data); + } else { + trace("\t\t0x%08x\n", data); + } + + init->offset += 4; + } + trace("\t}\n"); + addr += incr; + } +} + +/** + * INIT_COPY_ZM_REG - opcode 0x90 + * + */ +static void +init_copy_zm_reg(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 sreg = nvbios_rd32(bios, init->offset + 1); + u32 dreg = nvbios_rd32(bios, init->offset + 5); + + trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", dreg, sreg); + init->offset += 9; + + init_wr32(init, dreg, init_rd32(init, sreg)); +} + +/** + * INIT_ZM_REG_GROUP - opcode 0x91 + * + */ +static void +init_zm_reg_group(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 addr = nvbios_rd32(bios, init->offset + 1); + u8 count = nvbios_rd08(bios, init->offset + 5); + + trace("ZM_REG_GROUP\tR[0x%06x] =\n", addr); + init->offset += 6; + + while (count--) { + u32 data = nvbios_rd32(bios, init->offset); + trace("\t0x%08x\n", data); + init_wr32(init, addr, data); + init->offset += 4; + } +} + +/** + * INIT_XLAT - opcode 0x96 + * + */ +static void +init_xlat(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 saddr = nvbios_rd32(bios, init->offset + 1); + u8 sshift = nvbios_rd08(bios, init->offset + 5); + u8 smask = nvbios_rd08(bios, init->offset + 6); + u8 index = nvbios_rd08(bios, init->offset + 7); + u32 daddr = nvbios_rd32(bios, init->offset + 8); + u32 dmask = nvbios_rd32(bios, init->offset + 12); + u8 shift = nvbios_rd08(bios, init->offset + 16); + u32 data; + + trace("INIT_XLAT\tR[0x%06x] &= 0x%08x |= " + "(X%02x((R[0x%06x] %s 0x%02x) & 0x%02x) << 0x%02x)\n", + daddr, dmask, index, saddr, (sshift & 0x80) ? "<<" : ">>", + (sshift & 0x80) ? (0x100 - sshift) : sshift, smask, shift); + init->offset += 17; + + data = init_shift(init_rd32(init, saddr), sshift) & smask; + data = init_xlat_(init, index, data) << shift; + init_mask(init, daddr, ~dmask, data); +} + +/** + * INIT_ZM_MASK_ADD - opcode 0x97 + * + */ +static void +init_zm_mask_add(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 addr = nvbios_rd32(bios, init->offset + 1); + u32 mask = nvbios_rd32(bios, init->offset + 5); + u32 add = nvbios_rd32(bios, init->offset + 9); + u32 data; + + trace("ZM_MASK_ADD\tR[0x%06x] &= 0x%08x += 0x%08x\n", addr, mask, add); + init->offset += 13; + + data = init_rd32(init, addr); + data = (data & mask) | ((data + add) & ~mask); + init_wr32(init, addr, data); +} + +/** + * INIT_AUXCH - opcode 0x98 + * + */ +static void +init_auxch(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 addr = nvbios_rd32(bios, init->offset + 1); + u8 count = nvbios_rd08(bios, init->offset + 5); + + trace("AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count); + init->offset += 6; + + while (count--) { + u8 mask = nvbios_rd08(bios, init->offset + 0); + u8 data = nvbios_rd08(bios, init->offset + 1); + trace("\tAUX[0x%08x] &= 0x%02x |= 0x%02x\n", addr, mask, data); + mask = init_rdauxr(init, addr) & mask; + init_wrauxr(init, addr, mask | data); + init->offset += 2; + } +} + +/** + * INIT_AUXCH - opcode 0x99 + * + */ +static void +init_zm_auxch(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u32 addr = nvbios_rd32(bios, init->offset + 1); + u8 count = nvbios_rd08(bios, init->offset + 5); + + trace("ZM_AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count); + init->offset += 6; + + while (count--) { + u8 data = nvbios_rd08(bios, init->offset + 0); + trace("\tAUX[0x%08x] = 0x%02x\n", addr, data); + init_wrauxr(init, addr, data); + init->offset += 1; + } +} + +/** + * INIT_I2C_LONG_IF - opcode 0x9a + * + */ +static void +init_i2c_long_if(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + u8 index = nvbios_rd08(bios, init->offset + 1); + u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; + u8 reglo = nvbios_rd08(bios, init->offset + 3); + u8 reghi = nvbios_rd08(bios, init->offset + 4); + u8 mask = nvbios_rd08(bios, init->offset + 5); + u8 data = nvbios_rd08(bios, init->offset + 6); + struct i2c_adapter *adap; + + trace("I2C_LONG_IF\t" + "I2C[0x%02x][0x%02x][0x%02x%02x] & 0x%02x == 0x%02x\n", + index, addr, reglo, reghi, mask, data); + init->offset += 7; + + adap = init_i2c(init, index); + if (adap) { + u8 i[2] = { reghi, reglo }; + u8 o[1] = {}; + struct i2c_msg msg[] = { + { .addr = addr, .flags = 0, .len = 2, .buf = i }, + { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = o } + }; + int ret; + + ret = i2c_transfer(adap, msg, 2); + if (ret == 2 && ((o[0] & mask) == data)) + return; + } + + init_exec_set(init, false); +} + +/** + * INIT_GPIO_NE - opcode 0xa9 + * + */ +static void +init_gpio_ne(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + struct nvkm_gpio *gpio = bios->subdev.device->gpio; + struct dcb_gpio_func func; + u8 count = nvbios_rd08(bios, init->offset + 1); + u8 idx = 0, ver, len; + u16 data, i; + + trace("GPIO_NE\t"); + init->offset += 2; + + for (i = init->offset; i < init->offset + count; i++) + cont("0x%02x ", nvbios_rd08(bios, i)); + cont("\n"); + + while ((data = dcb_gpio_parse(bios, 0, idx++, &ver, &len, &func))) { + if (func.func != DCB_GPIO_UNUSED) { + for (i = init->offset; i < init->offset + count; i++) { + if (func.func == nvbios_rd08(bios, i)) + break; + } + + trace("\tFUNC[0x%02x]", func.func); + if (i == (init->offset + count)) { + cont(" *"); + if (init_exec(init)) + nvkm_gpio_reset(gpio, func.func); + } + cont("\n"); + } + } + + init->offset += count; +} + +static struct nvbios_init_opcode { + void (*exec)(struct nvbios_init *); +} init_opcode[] = { + [0x32] = { init_io_restrict_prog }, + [0x33] = { init_repeat }, + [0x34] = { init_io_restrict_pll }, + [0x36] = { init_end_repeat }, + [0x37] = { init_copy }, + [0x38] = { init_not }, + [0x39] = { init_io_flag_condition }, + [0x3a] = { init_generic_condition }, + [0x3b] = { init_io_mask_or }, + [0x3c] = { init_io_or }, + [0x47] = { init_andn_reg }, + [0x48] = { init_or_reg }, + [0x49] = { init_idx_addr_latched }, + [0x4a] = { init_io_restrict_pll2 }, + [0x4b] = { init_pll2 }, + [0x4c] = { init_i2c_byte }, + [0x4d] = { init_zm_i2c_byte }, + [0x4e] = { init_zm_i2c }, + [0x4f] = { init_tmds }, + [0x50] = { init_zm_tmds_group }, + [0x51] = { init_cr_idx_adr_latch }, + [0x52] = { init_cr }, + [0x53] = { init_zm_cr }, + [0x54] = { init_zm_cr_group }, + [0x56] = { init_condition_time }, + [0x57] = { init_ltime }, + [0x58] = { init_zm_reg_sequence }, + [0x59] = { init_pll_indirect }, + [0x5a] = { init_zm_reg_indirect }, + [0x5b] = { init_sub_direct }, + [0x5c] = { init_jump }, + [0x5e] = { init_i2c_if }, + [0x5f] = { init_copy_nv_reg }, + [0x62] = { init_zm_index_io }, + [0x63] = { init_compute_mem }, + [0x65] = { init_reset }, + [0x66] = { init_configure_mem }, + [0x67] = { init_configure_clk }, + [0x68] = { init_configure_preinit }, + [0x69] = { init_io }, + [0x6b] = { init_sub }, + [0x6d] = { init_ram_condition }, + [0x6e] = { init_nv_reg }, + [0x6f] = { init_macro }, + [0x71] = { init_done }, + [0x72] = { init_resume }, + [0x73] = { init_strap_condition }, + [0x74] = { init_time }, + [0x75] = { init_condition }, + [0x76] = { init_io_condition }, + [0x77] = { init_zm_reg16 }, + [0x78] = { init_index_io }, + [0x79] = { init_pll }, + [0x7a] = { init_zm_reg }, + [0x87] = { init_ram_restrict_pll }, + [0x8c] = { init_reset_begun }, + [0x8d] = { init_reset_end }, + [0x8e] = { init_gpio }, + [0x8f] = { init_ram_restrict_zm_reg_group }, + [0x90] = { init_copy_zm_reg }, + [0x91] = { init_zm_reg_group }, + [0x92] = { init_reserved }, + [0x96] = { init_xlat }, + [0x97] = { init_zm_mask_add }, + [0x98] = { init_auxch }, + [0x99] = { init_zm_auxch }, + [0x9a] = { init_i2c_long_if }, + [0xa9] = { init_gpio_ne }, + [0xaa] = { init_reserved }, +}; + +int +nvbios_exec(struct nvbios_init *init) +{ + struct nvkm_bios *bios = init->subdev->device->bios; + + init->nested++; + while (init->offset) { + u8 opcode = nvbios_rd08(bios, init->offset); + if (opcode >= ARRAY_SIZE(init_opcode) || + !init_opcode[opcode].exec) { + error("unknown opcode 0x%02x\n", opcode); + return -EINVAL; + } + + init_opcode[opcode].exec(init); + } + init->nested--; + return 0; +} + +int +nvbios_post(struct nvkm_subdev *subdev, bool execute) +{ + struct nvkm_bios *bios = subdev->device->bios; + int ret = 0; + int i = -1; + u16 data; + + if (execute) + nvkm_debug(subdev, "running init tables\n"); + while (!ret && (data = (init_script(bios, ++i)))) { + ret = nvbios_init(subdev, data, + init.execute = execute ? 1 : 0; + ); + } + + /* the vbios parser will run this right after the normal init + * tables, whereas the binary driver appears to run it later. + */ + if (!ret && (data = init_unknown_script(bios))) { + ret = nvbios_init(subdev, data, + init.execute = execute ? 1 : 0; + ); + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c new file mode 100644 index 000000000..994cc2d77 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c @@ -0,0 +1,137 @@ +/* + * 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 +#include +#include + +u16 +mxm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct bit_entry x; + + if (bit_entry(bios, 'x', &x)) { + nvkm_debug(subdev, "BIT 'x' table not present\n"); + return 0x0000; + } + + *ver = x.version; + *hdr = x.length; + if (*ver != 1 || *hdr < 3) { + nvkm_warn(subdev, "BIT 'x' table %d/%d unknown\n", *ver, *hdr); + return 0x0000; + } + + return x.offset; +} + +/* These map MXM v2.x digital connection values to the appropriate SOR/link, + * hopefully they're correct for all boards within the same chipset... + * + * MXM v3.x VBIOS are nicer and provide pointers to these tables. + */ +static u8 g84_sor_map[16] = { + 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 g92_sor_map[16] = { + 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 g94_sor_map[16] = { + 0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 g98_sor_map[16] = { + 0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31, + 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +u8 +mxm_sor_map(struct nvkm_bios *bios, u8 conn) +{ + struct nvkm_subdev *subdev = &bios->subdev; + u8 ver, hdr; + u16 mxm = mxm_table(bios, &ver, &hdr); + if (mxm && hdr >= 6) { + u16 map = nvbios_rd16(bios, mxm + 4); + if (map) { + ver = nvbios_rd08(bios, map); + if (ver == 0x10 || ver == 0x11) { + if (conn < nvbios_rd08(bios, map + 3)) { + map += nvbios_rd08(bios, map + 1); + map += conn; + return nvbios_rd08(bios, map); + } + + return 0x00; + } + + nvkm_warn(subdev, "unknown sor map v%02x\n", ver); + } + } + + if (bios->version.chip == 0x84 || bios->version.chip == 0x86) + return g84_sor_map[conn]; + if (bios->version.chip == 0x92) + return g92_sor_map[conn]; + if (bios->version.chip == 0x94 || bios->version.chip == 0x96) + return g94_sor_map[conn]; + if (bios->version.chip == 0x98) + return g98_sor_map[conn]; + + nvkm_warn(subdev, "missing sor map\n"); + return 0x00; +} + +u8 +mxm_ddc_map(struct nvkm_bios *bios, u8 port) +{ + struct nvkm_subdev *subdev = &bios->subdev; + u8 ver, hdr; + u16 mxm = mxm_table(bios, &ver, &hdr); + if (mxm && hdr >= 8) { + u16 map = nvbios_rd16(bios, mxm + 6); + if (map) { + ver = nvbios_rd08(bios, map); + if (ver == 0x10) { + if (port < nvbios_rd08(bios, map + 3)) { + map += nvbios_rd08(bios, map + 1); + map += port; + return nvbios_rd08(bios, map); + } + + return 0x00; + } + + nvkm_warn(subdev, "unknown ddc map v%02x\n", ver); + } + } + + /* v2.x: directly write port as dcb i2cidx */ + return (port << 4) | port; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c new file mode 100644 index 000000000..955df2963 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c @@ -0,0 +1,59 @@ +/* + * Copyright 2014 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 +#include +#include + +u32 +nvbios_npdeTe(struct nvkm_bios *bios, u32 base) +{ + struct nvbios_pcirT pcir; + u8 ver; u16 hdr; + u32 data = nvbios_pcirTp(bios, base, &ver, &hdr, &pcir); + if (data = (data + hdr + 0x0f) & ~0x0f, data) { + switch (nvbios_rd32(bios, data + 0x00)) { + case 0x4544504e: /* NPDE */ + break; + default: + nvkm_debug(&bios->subdev, + "%08x: NPDE signature (%08x) unknown\n", + data, nvbios_rd32(bios, data + 0x00)); + data = 0; + break; + } + } + return data; +} + +u32 +nvbios_npdeTp(struct nvkm_bios *bios, u32 base, struct nvbios_npdeT *info) +{ + u32 data = nvbios_npdeTe(bios, base); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->image_size = nvbios_rd16(bios, data + 0x08) * 512; + info->last = nvbios_rd08(bios, data + 0x0a) & 0x80; + } + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c new file mode 100644 index 000000000..67cb3aeb2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c @@ -0,0 +1,69 @@ +/* + * Copyright 2014 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 +#include + +u32 +nvbios_pcirTe(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr) +{ + u32 data = nvbios_rd16(bios, base + 0x18); + if (data) { + data += base; + switch (nvbios_rd32(bios, data + 0x00)) { + case 0x52494350: /* PCIR */ + case 0x53494752: /* RGIS */ + case 0x5344504e: /* NPDS */ + *hdr = nvbios_rd16(bios, data + 0x0a); + *ver = nvbios_rd08(bios, data + 0x0c); + break; + default: + nvkm_debug(&bios->subdev, + "%08x: PCIR signature (%08x) unknown\n", + data, nvbios_rd32(bios, data + 0x00)); + data = 0; + break; + } + } + return data; +} + +u32 +nvbios_pcirTp(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr, + struct nvbios_pcirT *info) +{ + u32 data = nvbios_pcirTe(bios, base, ver, hdr); + memset(info, 0x00, sizeof(*info)); + if (data) { + info->vendor_id = nvbios_rd16(bios, data + 0x04); + info->device_id = nvbios_rd16(bios, data + 0x06); + info->class_code[0] = nvbios_rd08(bios, data + 0x0d); + info->class_code[1] = nvbios_rd08(bios, data + 0x0e); + info->class_code[2] = nvbios_rd08(bios, data + 0x0f); + info->image_size = nvbios_rd16(bios, data + 0x10) * 512; + info->image_rev = nvbios_rd16(bios, data + 0x12); + info->image_type = nvbios_rd08(bios, data + 0x14); + info->last = nvbios_rd08(bios, data + 0x15) & 0x80; + } + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c new file mode 100644 index 000000000..f039388f0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c @@ -0,0 +1,216 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + */ +#include +#include +#include +#include + +u32 +nvbios_perf_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_P; + u32 perf = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version <= 2) { + perf = nvbios_rd32(bios, bit_P.offset + 0); + if (perf) { + *ver = nvbios_rd08(bios, perf + 0); + *hdr = nvbios_rd08(bios, perf + 1); + if (*ver >= 0x40 && *ver < 0x41) { + *cnt = nvbios_rd08(bios, perf + 5); + *len = nvbios_rd08(bios, perf + 2); + *snr = nvbios_rd08(bios, perf + 4); + *ssz = nvbios_rd08(bios, perf + 3); + return perf; + } else + if (*ver >= 0x20 && *ver < 0x40) { + *cnt = nvbios_rd08(bios, perf + 2); + *len = nvbios_rd08(bios, perf + 3); + *snr = nvbios_rd08(bios, perf + 4); + *ssz = nvbios_rd08(bios, perf + 5); + return perf; + } + } + } + } + + if (bios->bmp_offset) { + if (nvbios_rd08(bios, bios->bmp_offset + 6) >= 0x25) { + perf = nvbios_rd16(bios, bios->bmp_offset + 0x94); + if (perf) { + *hdr = nvbios_rd08(bios, perf + 0); + *ver = nvbios_rd08(bios, perf + 1); + *cnt = nvbios_rd08(bios, perf + 2); + *len = nvbios_rd08(bios, perf + 3); + *snr = 0; + *ssz = 0; + return perf; + } + } + } + + return 0; +} + +u32 +nvbios_perf_entry(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 perf = nvbios_perf_table(bios, ver, hdr, cnt, len, &snr, &ssz); + if (perf && idx < *cnt) { + perf = perf + *hdr + (idx * (*len + (snr * ssz))); + *hdr = *len; + *cnt = snr; + *len = ssz; + return perf; + } + return 0; +} + +u32 +nvbios_perfEp(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *info) +{ + u32 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + info->pstate = nvbios_rd08(bios, perf + 0x00); + switch (!!perf * *ver) { + case 0x12: + case 0x13: + case 0x14: + info->core = nvbios_rd32(bios, perf + 0x01) * 10; + info->memory = nvbios_rd32(bios, perf + 0x05) * 20; + info->fanspeed = nvbios_rd08(bios, perf + 0x37); + if (*hdr > 0x38) + info->voltage = nvbios_rd08(bios, perf + 0x38); + break; + case 0x21: + case 0x23: + case 0x24: + info->fanspeed = nvbios_rd08(bios, perf + 0x04); + info->voltage = nvbios_rd08(bios, perf + 0x05); + info->shader = nvbios_rd16(bios, perf + 0x06) * 1000; + info->core = info->shader + (signed char) + nvbios_rd08(bios, perf + 0x08) * 1000; + switch (bios->subdev.device->chipset) { + case 0x49: + case 0x4b: + info->memory = nvbios_rd16(bios, perf + 0x0b) * 1000; + break; + default: + info->memory = nvbios_rd16(bios, perf + 0x0b) * 2000; + break; + } + break; + case 0x25: + info->fanspeed = nvbios_rd08(bios, perf + 0x04); + info->voltage = nvbios_rd08(bios, perf + 0x05); + info->core = nvbios_rd16(bios, perf + 0x06) * 1000; + info->shader = nvbios_rd16(bios, perf + 0x0a) * 1000; + info->memory = nvbios_rd16(bios, perf + 0x0c) * 1000; + break; + case 0x30: + info->script = nvbios_rd16(bios, perf + 0x02); + fallthrough; + case 0x35: + info->fanspeed = nvbios_rd08(bios, perf + 0x06); + info->voltage = nvbios_rd08(bios, perf + 0x07); + info->core = nvbios_rd16(bios, perf + 0x08) * 1000; + info->shader = nvbios_rd16(bios, perf + 0x0a) * 1000; + info->memory = nvbios_rd16(bios, perf + 0x0c) * 1000; + info->vdec = nvbios_rd16(bios, perf + 0x10) * 1000; + info->disp = nvbios_rd16(bios, perf + 0x14) * 1000; + break; + case 0x40: + info->voltage = nvbios_rd08(bios, perf + 0x02); + switch (nvbios_rd08(bios, perf + 0xb) & 0x3) { + case 0: + info->pcie_speed = NVKM_PCIE_SPEED_5_0; + break; + case 3: + case 1: + info->pcie_speed = NVKM_PCIE_SPEED_2_5; + break; + case 2: + info->pcie_speed = NVKM_PCIE_SPEED_8_0; + break; + default: + break; + } + info->pcie_width = 0xff; + break; + default: + return 0; + } + return perf; +} + +u32 +nvbios_perfSe(struct nvkm_bios *bios, u32 perfE, int idx, + u8 *ver, u8 *hdr, u8 cnt, u8 len) +{ + u32 data = 0x00000000; + if (idx < cnt) { + data = perfE + *hdr + (idx * len); + *hdr = len; + } + return data; +} + +u32 +nvbios_perfSp(struct nvkm_bios *bios, u32 perfE, int idx, + u8 *ver, u8 *hdr, u8 cnt, u8 len, + struct nvbios_perfS *info) +{ + u32 data = nvbios_perfSe(bios, perfE, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x40: + info->v40.freq = (nvbios_rd16(bios, data + 0x00) & 0x3fff) * 1000; + break; + default: + break; + } + return data; +} + +int +nvbios_perf_fan_parse(struct nvkm_bios *bios, + struct nvbios_perf_fan *fan) +{ + u8 ver, hdr, cnt, len, snr, ssz; + u32 perf = nvbios_perf_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz); + if (!perf) + return -ENODEV; + + if (ver >= 0x20 && ver < 0x40 && hdr > 6) + fan->pwm_divisor = nvbios_rd16(bios, perf + 6); + else + fan->pwm_divisor = 0; + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c new file mode 100644 index 000000000..2ec84b8a3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c @@ -0,0 +1,440 @@ +/* + * Copyright 2005-2006 Erik Waling + * Copyright 2006 Stephane Marchesin + * Copyright 2007-2009 Stuart Bennett + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include + + +struct pll_mapping { + u8 type; + u32 reg; +}; + +static struct pll_mapping +nv04_pll_mapping[] = { + { PLL_CORE , 0x680500 }, + { PLL_MEMORY, 0x680504 }, + { PLL_VPLL0 , 0x680508 }, + { PLL_VPLL1 , 0x680520 }, + {} +}; + +static struct pll_mapping +nv40_pll_mapping[] = { + { PLL_CORE , 0x004000 }, + { PLL_MEMORY, 0x004020 }, + { PLL_VPLL0 , 0x680508 }, + { PLL_VPLL1 , 0x680520 }, + {} +}; + +static struct pll_mapping +nv50_pll_mapping[] = { + { PLL_CORE , 0x004028 }, + { PLL_SHADER, 0x004020 }, + { PLL_UNK03 , 0x004000 }, + { PLL_MEMORY, 0x004008 }, + { PLL_UNK40 , 0x00e810 }, + { PLL_UNK41 , 0x00e818 }, + { PLL_UNK42 , 0x00e824 }, + { PLL_VPLL0 , 0x614100 }, + { PLL_VPLL1 , 0x614900 }, + {} +}; + +static struct pll_mapping +g84_pll_mapping[] = { + { PLL_CORE , 0x004028 }, + { PLL_SHADER, 0x004020 }, + { PLL_MEMORY, 0x004008 }, + { PLL_VDEC , 0x004030 }, + { PLL_UNK41 , 0x00e818 }, + { PLL_VPLL0 , 0x614100 }, + { PLL_VPLL1 , 0x614900 }, + {} +}; + +static u32 +pll_limits_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_C; + u32 data = 0x0000; + + if (!bit_entry(bios, 'C', &bit_C)) { + if (bit_C.version == 1 && bit_C.length >= 10) + data = nvbios_rd16(bios, bit_C.offset + 8); + if (bit_C.version == 2 && bit_C.length >= 4) + data = nvbios_rd32(bios, bit_C.offset + 0); + if (data) { + *ver = nvbios_rd08(bios, data + 0); + *hdr = nvbios_rd08(bios, data + 1); + *len = nvbios_rd08(bios, data + 2); + *cnt = nvbios_rd08(bios, data + 3); + return data; + } + } + + if (bmp_version(bios) >= 0x0524) { + data = nvbios_rd16(bios, bios->bmp_offset + 142); + if (data) { + *ver = nvbios_rd08(bios, data + 0); + *hdr = 1; + *cnt = 1; + *len = 0x18; + return data; + } + } + + *ver = 0x00; + return data; +} + +static struct pll_mapping * +pll_map(struct nvkm_bios *bios) +{ + struct nvkm_device *device = bios->subdev.device; + switch (device->card_type) { + case NV_04: + case NV_10: + case NV_11: + case NV_20: + case NV_30: + return nv04_pll_mapping; + case NV_40: + return nv40_pll_mapping; + case NV_50: + if (device->chipset == 0x50) + return nv50_pll_mapping; + else + if (device->chipset < 0xa3 || + device->chipset == 0xaa || + device->chipset == 0xac) + return g84_pll_mapping; + fallthrough; + default: + return NULL; + } +} + +static u32 +pll_map_reg(struct nvkm_bios *bios, u32 reg, u32 *type, u8 *ver, u8 *len) +{ + struct pll_mapping *map; + u8 hdr, cnt; + u32 data; + + data = pll_limits_table(bios, ver, &hdr, &cnt, len); + if (data && *ver >= 0x30) { + data += hdr; + while (cnt--) { + if (nvbios_rd32(bios, data + 3) == reg) { + *type = nvbios_rd08(bios, data + 0); + return data; + } + data += *len; + } + return 0x0000; + } + + map = pll_map(bios); + while (map && map->reg) { + if (map->reg == reg && *ver >= 0x20) { + u32 addr = (data += hdr); + *type = map->type; + while (cnt--) { + if (nvbios_rd32(bios, data) == map->reg) + return data; + data += *len; + } + return addr; + } else + if (map->reg == reg) { + *type = map->type; + return data + 1; + } + map++; + } + + return 0x0000; +} + +static u32 +pll_map_type(struct nvkm_bios *bios, u8 type, u32 *reg, u8 *ver, u8 *len) +{ + struct pll_mapping *map; + u8 hdr, cnt; + u32 data; + + data = pll_limits_table(bios, ver, &hdr, &cnt, len); + if (data && *ver >= 0x30) { + data += hdr; + while (cnt--) { + if (nvbios_rd08(bios, data + 0) == type) { + if (*ver < 0x50) + *reg = nvbios_rd32(bios, data + 3); + else + *reg = 0; + return data; + } + data += *len; + } + return 0x0000; + } + + map = pll_map(bios); + while (map && map->reg) { + if (map->type == type && *ver >= 0x20) { + u32 addr = (data += hdr); + *reg = map->reg; + while (cnt--) { + if (nvbios_rd32(bios, data) == map->reg) + return data; + data += *len; + } + return addr; + } else + if (map->type == type) { + *reg = map->reg; + return data + 1; + } + map++; + } + + return 0x0000; +} + +int +nvbios_pll_parse(struct nvkm_bios *bios, u32 type, struct nvbios_pll *info) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvkm_device *device = subdev->device; + u8 ver, len; + u32 reg = type; + u32 data; + + if (type > PLL_MAX) { + reg = type; + data = pll_map_reg(bios, reg, &type, &ver, &len); + } else { + data = pll_map_type(bios, type, ®, &ver, &len); + } + + if (ver && !data) + return -ENOENT; + + memset(info, 0, sizeof(*info)); + info->type = type; + info->reg = reg; + + switch (ver) { + case 0x00: + break; + case 0x10: + case 0x11: + info->vco1.min_freq = nvbios_rd32(bios, data + 0); + info->vco1.max_freq = nvbios_rd32(bios, data + 4); + info->vco2.min_freq = nvbios_rd32(bios, data + 8); + info->vco2.max_freq = nvbios_rd32(bios, data + 12); + info->vco1.min_inputfreq = nvbios_rd32(bios, data + 16); + info->vco2.min_inputfreq = nvbios_rd32(bios, data + 20); + info->vco1.max_inputfreq = INT_MAX; + info->vco2.max_inputfreq = INT_MAX; + + info->max_p = 0x7; + info->max_p_usable = 0x6; + + /* these values taken from nv30/31/36 */ + switch (bios->version.chip) { + case 0x36: + info->vco1.min_n = 0x5; + break; + default: + info->vco1.min_n = 0x1; + break; + } + info->vco1.max_n = 0xff; + info->vco1.min_m = 0x1; + info->vco1.max_m = 0xd; + + /* + * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this + * table version (apart from nv35)), N2 is compared to + * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and + * save a comparison + */ + info->vco2.min_n = 0x4; + switch (bios->version.chip) { + case 0x30: + case 0x35: + info->vco2.max_n = 0x1f; + break; + default: + info->vco2.max_n = 0x28; + break; + } + info->vco2.min_m = 0x1; + info->vco2.max_m = 0x4; + break; + case 0x20: + case 0x21: + info->vco1.min_freq = nvbios_rd16(bios, data + 4) * 1000; + info->vco1.max_freq = nvbios_rd16(bios, data + 6) * 1000; + info->vco2.min_freq = nvbios_rd16(bios, data + 8) * 1000; + info->vco2.max_freq = nvbios_rd16(bios, data + 10) * 1000; + info->vco1.min_inputfreq = nvbios_rd16(bios, data + 12) * 1000; + info->vco2.min_inputfreq = nvbios_rd16(bios, data + 14) * 1000; + info->vco1.max_inputfreq = nvbios_rd16(bios, data + 16) * 1000; + info->vco2.max_inputfreq = nvbios_rd16(bios, data + 18) * 1000; + info->vco1.min_n = nvbios_rd08(bios, data + 20); + info->vco1.max_n = nvbios_rd08(bios, data + 21); + info->vco1.min_m = nvbios_rd08(bios, data + 22); + info->vco1.max_m = nvbios_rd08(bios, data + 23); + info->vco2.min_n = nvbios_rd08(bios, data + 24); + info->vco2.max_n = nvbios_rd08(bios, data + 25); + info->vco2.min_m = nvbios_rd08(bios, data + 26); + info->vco2.max_m = nvbios_rd08(bios, data + 27); + + info->max_p = nvbios_rd08(bios, data + 29); + info->max_p_usable = info->max_p; + if (bios->version.chip < 0x60) + info->max_p_usable = 0x6; + info->bias_p = nvbios_rd08(bios, data + 30); + + if (len > 0x22) + info->refclk = nvbios_rd32(bios, data + 31); + break; + case 0x30: + data = nvbios_rd16(bios, data + 1); + + info->vco1.min_freq = nvbios_rd16(bios, data + 0) * 1000; + info->vco1.max_freq = nvbios_rd16(bios, data + 2) * 1000; + info->vco2.min_freq = nvbios_rd16(bios, data + 4) * 1000; + info->vco2.max_freq = nvbios_rd16(bios, data + 6) * 1000; + info->vco1.min_inputfreq = nvbios_rd16(bios, data + 8) * 1000; + info->vco2.min_inputfreq = nvbios_rd16(bios, data + 10) * 1000; + info->vco1.max_inputfreq = nvbios_rd16(bios, data + 12) * 1000; + info->vco2.max_inputfreq = nvbios_rd16(bios, data + 14) * 1000; + info->vco1.min_n = nvbios_rd08(bios, data + 16); + info->vco1.max_n = nvbios_rd08(bios, data + 17); + info->vco1.min_m = nvbios_rd08(bios, data + 18); + info->vco1.max_m = nvbios_rd08(bios, data + 19); + info->vco2.min_n = nvbios_rd08(bios, data + 20); + info->vco2.max_n = nvbios_rd08(bios, data + 21); + info->vco2.min_m = nvbios_rd08(bios, data + 22); + info->vco2.max_m = nvbios_rd08(bios, data + 23); + info->max_p_usable = info->max_p = nvbios_rd08(bios, data + 25); + info->bias_p = nvbios_rd08(bios, data + 27); + info->refclk = nvbios_rd32(bios, data + 28); + break; + case 0x40: + info->refclk = nvbios_rd16(bios, data + 9) * 1000; + data = nvbios_rd16(bios, data + 1); + + info->vco1.min_freq = nvbios_rd16(bios, data + 0) * 1000; + info->vco1.max_freq = nvbios_rd16(bios, data + 2) * 1000; + info->vco1.min_inputfreq = nvbios_rd16(bios, data + 4) * 1000; + info->vco1.max_inputfreq = nvbios_rd16(bios, data + 6) * 1000; + info->vco1.min_m = nvbios_rd08(bios, data + 8); + info->vco1.max_m = nvbios_rd08(bios, data + 9); + info->vco1.min_n = nvbios_rd08(bios, data + 10); + info->vco1.max_n = nvbios_rd08(bios, data + 11); + info->min_p = nvbios_rd08(bios, data + 12); + info->max_p = nvbios_rd08(bios, data + 13); + break; + case 0x50: + info->refclk = nvbios_rd16(bios, data + 1) * 1000; + /* info->refclk_alt = nvbios_rd16(bios, data + 3) * 1000; */ + info->vco1.min_freq = nvbios_rd16(bios, data + 5) * 1000; + info->vco1.max_freq = nvbios_rd16(bios, data + 7) * 1000; + info->vco1.min_inputfreq = nvbios_rd16(bios, data + 9) * 1000; + info->vco1.max_inputfreq = nvbios_rd16(bios, data + 11) * 1000; + info->vco1.min_m = nvbios_rd08(bios, data + 13); + info->vco1.max_m = nvbios_rd08(bios, data + 14); + info->vco1.min_n = nvbios_rd08(bios, data + 15); + info->vco1.max_n = nvbios_rd08(bios, data + 16); + info->min_p = nvbios_rd08(bios, data + 17); + info->max_p = nvbios_rd08(bios, data + 18); + break; + default: + nvkm_error(subdev, "unknown pll limits version 0x%02x\n", ver); + return -EINVAL; + } + + if (!info->refclk) { + info->refclk = device->crystal; + if (bios->version.chip == 0x51) { + u32 sel_clk = nvkm_rd32(device, 0x680524); + if ((info->reg == 0x680508 && sel_clk & 0x20) || + (info->reg == 0x680520 && sel_clk & 0x80)) { + if (nvkm_rdvgac(device, 0, 0x27) < 0xa3) + info->refclk = 200000; + else + info->refclk = 25000; + } + } + } + + /* + * By now any valid limit table ought to have set a max frequency for + * vco1, so if it's zero it's either a pre limit table bios, or one + * with an empty limit table (seen on nv18) + */ + if (!info->vco1.max_freq) { + info->vco1.max_freq = nvbios_rd32(bios, bios->bmp_offset + 67); + info->vco1.min_freq = nvbios_rd32(bios, bios->bmp_offset + 71); + if (bmp_version(bios) < 0x0506) { + info->vco1.max_freq = 256000; + info->vco1.min_freq = 128000; + } + + info->vco1.min_inputfreq = 0; + info->vco1.max_inputfreq = INT_MAX; + info->vco1.min_n = 0x1; + info->vco1.max_n = 0xff; + info->vco1.min_m = 0x1; + + if (device->crystal == 13500) { + /* nv05 does this, nv11 doesn't, nv10 unknown */ + if (bios->version.chip < 0x11) + info->vco1.min_m = 0x7; + info->vco1.max_m = 0xd; + } else { + if (bios->version.chip < 0x11) + info->vco1.min_m = 0x8; + info->vco1.max_m = 0xe; + } + + if (bios->version.chip < 0x17 || + bios->version.chip == 0x1a || + bios->version.chip == 0x20) + info->max_p = 4; + else + info->max_p = 5; + info->max_p_usable = info->max_p; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c new file mode 100644 index 000000000..49e2664a7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c @@ -0,0 +1,102 @@ +/* + * Copyright 2014 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 +#include +#include +#include + +u32 +nvbios_pmuTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_p; + u32 data = 0; + + if (!bit_entry(bios, 'p', &bit_p)) { + if (bit_p.version == 2 && bit_p.length >= 4) + data = nvbios_rd32(bios, bit_p.offset + 0x00); + if (data) { + *ver = nvbios_rd08(bios, data + 0x00); /* maybe? */ + *hdr = nvbios_rd08(bios, data + 0x01); + *len = nvbios_rd08(bios, data + 0x02); + *cnt = nvbios_rd08(bios, data + 0x03); + } + } + + return data; +} + +u32 +nvbios_pmuEe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr) +{ + u8 cnt, len; + u32 data = nvbios_pmuTe(bios, ver, hdr, &cnt, &len); + if (data && idx < cnt) { + data = data + *hdr + (idx * len); + *hdr = len; + return data; + } + return 0; +} + +u32 +nvbios_pmuEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, + struct nvbios_pmuE *info) +{ + u32 data = nvbios_pmuEe(bios, idx, ver, hdr); + if (data) { + info->type = nvbios_rd08(bios, data + 0x00); + info->data = nvbios_rd32(bios, data + 0x02); + } + return data; +} + +bool +nvbios_pmuRm(struct nvkm_bios *bios, u8 type, struct nvbios_pmuR *info) +{ + struct nvbios_pmuE pmuE; + u8 ver, hdr, idx = 0; + u32 data; + memset(info, 0x00, sizeof(*info)); + while ((data = nvbios_pmuEp(bios, idx++, &ver, &hdr, &pmuE))) { + if (pmuE.type == type && (data = pmuE.data)) { + info->init_addr_pmu = nvbios_rd32(bios, data + 0x08); + info->args_addr_pmu = nvbios_rd32(bios, data + 0x0c); + info->boot_addr = data + 0x30; + info->boot_addr_pmu = nvbios_rd32(bios, data + 0x10) + + nvbios_rd32(bios, data + 0x18); + info->boot_size = nvbios_rd32(bios, data + 0x1c) - + nvbios_rd32(bios, data + 0x18); + info->code_addr = info->boot_addr + info->boot_size; + info->code_addr_pmu = info->boot_addr_pmu + + info->boot_size; + info->code_size = nvbios_rd32(bios, data + 0x20); + info->data_addr = data + 0x30 + + nvbios_rd32(bios, data + 0x24); + info->data_addr_pmu = nvbios_rd32(bios, data + 0x28); + info->data_size = nvbios_rd32(bios, data + 0x2c); + return true; + } + } + return false; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c new file mode 100644 index 000000000..03d2f970a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c @@ -0,0 +1,126 @@ +/* + * Copyright 2016 Karol Herbst + * + * 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: Karol Herbst + */ +#include +#include +#include + +static u32 +nvbios_power_budget_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, + u8 *len) +{ + struct bit_entry bit_P; + u32 power_budget; + + if (bit_entry(bios, 'P', &bit_P) || bit_P.version != 2 || + bit_P.length < 0x30) + return 0; + + power_budget = nvbios_rd32(bios, bit_P.offset + 0x2c); + if (!power_budget) + return 0; + + *ver = nvbios_rd08(bios, power_budget); + switch (*ver) { + case 0x20: + case 0x30: + *hdr = nvbios_rd08(bios, power_budget + 0x1); + *len = nvbios_rd08(bios, power_budget + 0x2); + *cnt = nvbios_rd08(bios, power_budget + 0x3); + return power_budget; + default: + break; + } + + return 0; +} + +int +nvbios_power_budget_header(struct nvkm_bios *bios, + struct nvbios_power_budget *budget) +{ + struct nvkm_subdev *subdev = &bios->subdev; + u8 ver, hdr, cnt, len, cap_entry; + u32 header; + + if (!bios || !budget) + return -EINVAL; + + header = nvbios_power_budget_table(bios, &ver, &hdr, &cnt, &len); + if (!header || !cnt) + return -ENODEV; + + switch (ver) { + case 0x20: + cap_entry = nvbios_rd08(bios, header + 0x9); + break; + case 0x30: + cap_entry = nvbios_rd08(bios, header + 0xa); + break; + default: + cap_entry = 0xff; + } + + if (cap_entry >= cnt && cap_entry != 0xff) { + nvkm_warn(subdev, + "invalid cap_entry in power budget table found\n"); + budget->cap_entry = 0xff; + return -EINVAL; + } + + budget->offset = header; + budget->ver = ver; + budget->hlen = hdr; + budget->elen = len; + budget->ecount = cnt; + + budget->cap_entry = cap_entry; + + return 0; +} + +int +nvbios_power_budget_entry(struct nvkm_bios *bios, + struct nvbios_power_budget *budget, + u8 idx, struct nvbios_power_budget_entry *entry) +{ + u32 entry_offset; + + if (!bios || !budget || !budget->offset || idx >= budget->ecount + || !entry) + return -EINVAL; + + entry_offset = budget->offset + budget->hlen + idx * budget->elen; + + if (budget->ver >= 0x20) { + entry->min_w = nvbios_rd32(bios, entry_offset + 0x2); + entry->avg_w = nvbios_rd32(bios, entry_offset + 0x6); + entry->max_w = nvbios_rd32(bios, entry_offset + 0xa); + } else { + entry->min_w = 0; + entry->max_w = nvbios_rd32(bios, entry_offset + 0x2); + entry->avg_w = entry->max_w; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h new file mode 100644 index 000000000..cfa8a0c35 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_BIOS_PRIV_H__ +#define __NVKM_BIOS_PRIV_H__ +#define nvkm_bios(p) container_of((p), struct nvkm_bios, subdev) +#include + +struct nvbios_source { + const char *name; + void *(*init)(struct nvkm_bios *, const char *); + void (*fini)(void *); + u32 (*read)(void *, u32 offset, u32 length, struct nvkm_bios *); + u32 (*size)(void *); + bool rw; + bool ignore_checksum; + bool no_pcir; + bool require_checksum; +}; + +int nvbios_extend(struct nvkm_bios *, u32 length); +int nvbios_shadow(struct nvkm_bios *); + +extern const struct nvbios_source nvbios_prom; +extern const struct nvbios_source nvbios_ramin; +extern const struct nvbios_source nvbios_acpi_fast; +extern const struct nvbios_source nvbios_acpi_slow; +extern const struct nvbios_source nvbios_pcirom; +extern const struct nvbios_source nvbios_platform; +extern const struct nvbios_source nvbios_of; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c new file mode 100644 index 000000000..d5222af10 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c @@ -0,0 +1,78 @@ +/* + * 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 +#include +#include +#include + +static u8 +nvbios_ramcfg_strap(struct nvkm_subdev *subdev) +{ + return (nvkm_rd32(subdev->device, 0x101000) & 0x0000003c) >> 2; +} + +u8 +nvbios_ramcfg_count(struct nvkm_bios *bios) +{ + struct bit_entry bit_M; + + if (!bit_entry(bios, 'M', &bit_M)) { + if (bit_M.version == 1 && bit_M.length >= 5) + return nvbios_rd08(bios, bit_M.offset + 2); + if (bit_M.version == 2 && bit_M.length >= 3) + return nvbios_rd08(bios, bit_M.offset + 0); + } + + return 0x00; +} + +u8 +nvbios_ramcfg_index(struct nvkm_subdev *subdev) +{ + struct nvkm_bios *bios = subdev->device->bios; + u8 strap = nvbios_ramcfg_strap(subdev); + u32 xlat = 0x00000000; + struct bit_entry bit_M; + struct nvbios_M0203E M0203E; + u8 ver, hdr; + + if (!bit_entry(bios, 'M', &bit_M)) { + if (bit_M.version == 1 && bit_M.length >= 5) + xlat = nvbios_rd16(bios, bit_M.offset + 3); + if (bit_M.version == 2 && bit_M.length >= 3) { + /*XXX: is M ever shorter than this? + * if not - what is xlat used for now? + * also - sigh.. + */ + if (bit_M.length >= 7 && + nvbios_M0203Em(bios, strap, &ver, &hdr, &M0203E)) + return M0203E.group; + xlat = nvbios_rd16(bios, bit_M.offset + 1); + } + } + + if (xlat) + strap = nvbios_rd08(bios, xlat + strap); + return strap; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c new file mode 100644 index 000000000..b57c370c7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c @@ -0,0 +1,258 @@ +/* + * 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 +#include +#include + +u32 +nvbios_rammapTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_P; + u32 rammap = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) + rammap = nvbios_rd32(bios, bit_P.offset + 4); + + if (rammap) { + *ver = nvbios_rd08(bios, rammap + 0); + switch (*ver) { + case 0x10: + case 0x11: + *hdr = nvbios_rd08(bios, rammap + 1); + *cnt = nvbios_rd08(bios, rammap + 5); + *len = nvbios_rd08(bios, rammap + 2); + *snr = nvbios_rd08(bios, rammap + 4); + *ssz = nvbios_rd08(bios, rammap + 3); + return rammap; + default: + break; + } + } + } + + return 0x0000; +} + +u32 +nvbios_rammapEe(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 rammap = nvbios_rammapTe(bios, ver, hdr, cnt, len, &snr, &ssz); + if (rammap && idx < *cnt) { + rammap = rammap + *hdr + (idx * (*len + (snr * ssz))); + *hdr = *len; + *cnt = snr; + *len = ssz; + return rammap; + } + return 0x0000; +} + +/* Pretend a performance mode is also a rammap entry, helps coalesce entries + * later on */ +u32 +nvbios_rammapEp_from_perf(struct nvkm_bios *bios, u32 data, u8 size, + struct nvbios_ramcfg *p) +{ + memset(p, 0x00, sizeof(*p)); + + p->rammap_00_16_20 = (nvbios_rd08(bios, data + 0x16) & 0x20) >> 5; + p->rammap_00_16_40 = (nvbios_rd08(bios, data + 0x16) & 0x40) >> 6; + p->rammap_00_17_02 = (nvbios_rd08(bios, data + 0x17) & 0x02) >> 1; + + return data; +} + +u32 +nvbios_rammapEp(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p) +{ + u32 data = nvbios_rammapEe(bios, idx, ver, hdr, cnt, len), temp; + memset(p, 0x00, sizeof(*p)); + p->rammap_ver = *ver; + p->rammap_hdr = *hdr; + switch (!!data * *ver) { + case 0x10: + p->rammap_min = nvbios_rd16(bios, data + 0x00); + p->rammap_max = nvbios_rd16(bios, data + 0x02); + p->rammap_10_04_02 = (nvbios_rd08(bios, data + 0x04) & 0x02) >> 1; + p->rammap_10_04_08 = (nvbios_rd08(bios, data + 0x04) & 0x08) >> 3; + break; + case 0x11: + p->rammap_min = nvbios_rd16(bios, data + 0x00); + p->rammap_max = nvbios_rd16(bios, data + 0x02); + p->rammap_11_08_01 = (nvbios_rd08(bios, data + 0x08) & 0x01) >> 0; + p->rammap_11_08_0c = (nvbios_rd08(bios, data + 0x08) & 0x0c) >> 2; + p->rammap_11_08_10 = (nvbios_rd08(bios, data + 0x08) & 0x10) >> 4; + temp = nvbios_rd32(bios, data + 0x09); + p->rammap_11_09_01ff = (temp & 0x000001ff) >> 0; + p->rammap_11_0a_03fe = (temp & 0x0003fe00) >> 9; + p->rammap_11_0a_0400 = (temp & 0x00040000) >> 18; + p->rammap_11_0a_0800 = (temp & 0x00080000) >> 19; + p->rammap_11_0b_01f0 = (temp & 0x01f00000) >> 20; + p->rammap_11_0b_0200 = (temp & 0x02000000) >> 25; + p->rammap_11_0b_0400 = (temp & 0x04000000) >> 26; + p->rammap_11_0b_0800 = (temp & 0x08000000) >> 27; + p->rammap_11_0d = nvbios_rd08(bios, data + 0x0d); + p->rammap_11_0e = nvbios_rd08(bios, data + 0x0e); + p->rammap_11_0f = nvbios_rd08(bios, data + 0x0f); + p->rammap_11_11_0c = (nvbios_rd08(bios, data + 0x11) & 0x0c) >> 2; + break; + default: + data = 0; + break; + } + return data; +} + +u32 +nvbios_rammapEm(struct nvkm_bios *bios, u16 mhz, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *info) +{ + int idx = 0; + u32 data; + while ((data = nvbios_rammapEp(bios, idx++, ver, hdr, cnt, len, info))) { + if (mhz >= info->rammap_min && mhz <= info->rammap_max) + break; + } + return data; +} + +u32 +nvbios_rammapSe(struct nvkm_bios *bios, u32 data, + u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, u8 *ver, u8 *hdr) +{ + if (idx < ecnt) { + data = data + ehdr + (idx * elen); + *ver = ever; + *hdr = elen; + return data; + } + return 0; +} + +u32 +nvbios_rammapSp_from_perf(struct nvkm_bios *bios, u32 data, u8 size, int idx, + struct nvbios_ramcfg *p) +{ + data += (idx * size); + + if (size < 11) + return 0x00000000; + + p->ramcfg_ver = 0; + p->ramcfg_timing = nvbios_rd08(bios, data + 0x01); + p->ramcfg_00_03_01 = (nvbios_rd08(bios, data + 0x03) & 0x01) >> 0; + p->ramcfg_00_03_02 = (nvbios_rd08(bios, data + 0x03) & 0x02) >> 1; + p->ramcfg_DLLoff = (nvbios_rd08(bios, data + 0x03) & 0x04) >> 2; + p->ramcfg_00_03_08 = (nvbios_rd08(bios, data + 0x03) & 0x08) >> 3; + p->ramcfg_RON = (nvbios_rd08(bios, data + 0x03) & 0x10) >> 3; + p->ramcfg_FBVDDQ = (nvbios_rd08(bios, data + 0x03) & 0x80) >> 7; + p->ramcfg_00_04_02 = (nvbios_rd08(bios, data + 0x04) & 0x02) >> 1; + p->ramcfg_00_04_04 = (nvbios_rd08(bios, data + 0x04) & 0x04) >> 2; + p->ramcfg_00_04_20 = (nvbios_rd08(bios, data + 0x04) & 0x20) >> 5; + p->ramcfg_00_05 = (nvbios_rd08(bios, data + 0x05) & 0xff) >> 0; + p->ramcfg_00_06 = (nvbios_rd08(bios, data + 0x06) & 0xff) >> 0; + p->ramcfg_00_07 = (nvbios_rd08(bios, data + 0x07) & 0xff) >> 0; + p->ramcfg_00_08 = (nvbios_rd08(bios, data + 0x08) & 0xff) >> 0; + p->ramcfg_00_09 = (nvbios_rd08(bios, data + 0x09) & 0xff) >> 0; + p->ramcfg_00_0a_0f = (nvbios_rd08(bios, data + 0x0a) & 0x0f) >> 0; + p->ramcfg_00_0a_f0 = (nvbios_rd08(bios, data + 0x0a) & 0xf0) >> 4; + + return data; +} + +u32 +nvbios_rammapSp(struct nvkm_bios *bios, u32 data, + u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, + u8 *ver, u8 *hdr, struct nvbios_ramcfg *p) +{ + data = nvbios_rammapSe(bios, data, ever, ehdr, ecnt, elen, idx, ver, hdr); + p->ramcfg_ver = *ver; + p->ramcfg_hdr = *hdr; + switch (!!data * *ver) { + case 0x10: + p->ramcfg_timing = nvbios_rd08(bios, data + 0x01); + p->ramcfg_10_02_01 = (nvbios_rd08(bios, data + 0x02) & 0x01) >> 0; + p->ramcfg_10_02_02 = (nvbios_rd08(bios, data + 0x02) & 0x02) >> 1; + p->ramcfg_10_02_04 = (nvbios_rd08(bios, data + 0x02) & 0x04) >> 2; + p->ramcfg_10_02_08 = (nvbios_rd08(bios, data + 0x02) & 0x08) >> 3; + p->ramcfg_10_02_10 = (nvbios_rd08(bios, data + 0x02) & 0x10) >> 4; + p->ramcfg_10_02_20 = (nvbios_rd08(bios, data + 0x02) & 0x20) >> 5; + p->ramcfg_DLLoff = (nvbios_rd08(bios, data + 0x02) & 0x40) >> 6; + p->ramcfg_10_03_0f = (nvbios_rd08(bios, data + 0x03) & 0x0f) >> 0; + p->ramcfg_10_04_01 = (nvbios_rd08(bios, data + 0x04) & 0x01) >> 0; + p->ramcfg_FBVDDQ = (nvbios_rd08(bios, data + 0x04) & 0x08) >> 3; + p->ramcfg_10_05 = (nvbios_rd08(bios, data + 0x05) & 0xff) >> 0; + p->ramcfg_10_06 = (nvbios_rd08(bios, data + 0x06) & 0xff) >> 0; + p->ramcfg_10_07 = (nvbios_rd08(bios, data + 0x07) & 0xff) >> 0; + p->ramcfg_10_08 = (nvbios_rd08(bios, data + 0x08) & 0xff) >> 0; + p->ramcfg_10_09_0f = (nvbios_rd08(bios, data + 0x09) & 0x0f) >> 0; + p->ramcfg_10_09_f0 = (nvbios_rd08(bios, data + 0x09) & 0xf0) >> 4; + break; + case 0x11: + p->ramcfg_timing = nvbios_rd08(bios, data + 0x00); + p->ramcfg_11_01_01 = (nvbios_rd08(bios, data + 0x01) & 0x01) >> 0; + p->ramcfg_11_01_02 = (nvbios_rd08(bios, data + 0x01) & 0x02) >> 1; + p->ramcfg_11_01_04 = (nvbios_rd08(bios, data + 0x01) & 0x04) >> 2; + p->ramcfg_11_01_08 = (nvbios_rd08(bios, data + 0x01) & 0x08) >> 3; + p->ramcfg_11_01_10 = (nvbios_rd08(bios, data + 0x01) & 0x10) >> 4; + p->ramcfg_DLLoff = (nvbios_rd08(bios, data + 0x01) & 0x20) >> 5; + p->ramcfg_11_01_40 = (nvbios_rd08(bios, data + 0x01) & 0x40) >> 6; + p->ramcfg_11_01_80 = (nvbios_rd08(bios, data + 0x01) & 0x80) >> 7; + p->ramcfg_11_02_03 = (nvbios_rd08(bios, data + 0x02) & 0x03) >> 0; + p->ramcfg_11_02_04 = (nvbios_rd08(bios, data + 0x02) & 0x04) >> 2; + p->ramcfg_11_02_08 = (nvbios_rd08(bios, data + 0x02) & 0x08) >> 3; + p->ramcfg_11_02_10 = (nvbios_rd08(bios, data + 0x02) & 0x10) >> 4; + p->ramcfg_11_02_40 = (nvbios_rd08(bios, data + 0x02) & 0x40) >> 6; + p->ramcfg_11_02_80 = (nvbios_rd08(bios, data + 0x02) & 0x80) >> 7; + p->ramcfg_11_03_0f = (nvbios_rd08(bios, data + 0x03) & 0x0f) >> 0; + p->ramcfg_11_03_30 = (nvbios_rd08(bios, data + 0x03) & 0x30) >> 4; + p->ramcfg_11_03_c0 = (nvbios_rd08(bios, data + 0x03) & 0xc0) >> 6; + p->ramcfg_11_03_f0 = (nvbios_rd08(bios, data + 0x03) & 0xf0) >> 4; + p->ramcfg_11_04 = (nvbios_rd08(bios, data + 0x04) & 0xff) >> 0; + p->ramcfg_11_06 = (nvbios_rd08(bios, data + 0x06) & 0xff) >> 0; + p->ramcfg_11_07_02 = (nvbios_rd08(bios, data + 0x07) & 0x02) >> 1; + p->ramcfg_11_07_04 = (nvbios_rd08(bios, data + 0x07) & 0x04) >> 2; + p->ramcfg_11_07_08 = (nvbios_rd08(bios, data + 0x07) & 0x08) >> 3; + p->ramcfg_11_07_10 = (nvbios_rd08(bios, data + 0x07) & 0x10) >> 4; + p->ramcfg_11_07_40 = (nvbios_rd08(bios, data + 0x07) & 0x40) >> 6; + p->ramcfg_11_07_80 = (nvbios_rd08(bios, data + 0x07) & 0x80) >> 7; + p->ramcfg_11_08_01 = (nvbios_rd08(bios, data + 0x08) & 0x01) >> 0; + p->ramcfg_11_08_02 = (nvbios_rd08(bios, data + 0x08) & 0x02) >> 1; + p->ramcfg_11_08_04 = (nvbios_rd08(bios, data + 0x08) & 0x04) >> 2; + p->ramcfg_11_08_08 = (nvbios_rd08(bios, data + 0x08) & 0x08) >> 3; + p->ramcfg_11_08_10 = (nvbios_rd08(bios, data + 0x08) & 0x10) >> 4; + p->ramcfg_11_08_20 = (nvbios_rd08(bios, data + 0x08) & 0x20) >> 5; + p->ramcfg_11_09 = (nvbios_rd08(bios, data + 0x09) & 0xff) >> 0; + break; + default: + data = 0; + break; + } + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c new file mode 100644 index 000000000..19188683c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c @@ -0,0 +1,242 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include +#include + +struct shadow { + u32 skip; + const struct nvbios_source *func; + void *data; + u32 size; + int score; +}; + +static bool +shadow_fetch(struct nvkm_bios *bios, struct shadow *mthd, u32 upto) +{ + const u32 limit = (upto + 3) & ~3; + const u32 start = bios->size; + void *data = mthd->data; + if (nvbios_extend(bios, limit) > 0) { + u32 read = mthd->func->read(data, start, limit - start, bios); + bios->size = start + read; + } + return bios->size >= upto; +} + +static int +shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvbios_image image; + int score = 1; + + if (mthd->func->no_pcir) { + image.base = 0; + image.type = 0; + image.size = mthd->func->size(mthd->data); + image.last = 1; + } else { + if (!shadow_fetch(bios, mthd, offset + 0x1000)) { + nvkm_debug(subdev, "%08x: header fetch failed\n", + offset); + return 0; + } + + if (!nvbios_image(bios, idx, &image)) { + nvkm_debug(subdev, "image %d invalid\n", idx); + return 0; + } + } + nvkm_debug(subdev, "%08x: type %02x, %d bytes\n", + image.base, image.type, image.size); + + if (!shadow_fetch(bios, mthd, image.base + image.size)) { + nvkm_debug(subdev, "%08x: fetch failed\n", image.base); + return 0; + } + + switch (image.type) { + case 0x00: + if (!mthd->func->ignore_checksum && + nvbios_checksum(&bios->data[image.base], image.size)) { + nvkm_debug(subdev, "%08x: checksum failed\n", + image.base); + if (!mthd->func->require_checksum) { + if (mthd->func->rw) + score += 1; + score += 1; + } else + return 0; + } else { + score += 3; + } + break; + default: + score += 3; + break; + } + + if (!image.last) + score += shadow_image(bios, idx + 1, offset + image.size, mthd); + return score; +} + +static int +shadow_method(struct nvkm_bios *bios, struct shadow *mthd, const char *name) +{ + const struct nvbios_source *func = mthd->func; + struct nvkm_subdev *subdev = &bios->subdev; + if (func->name) { + nvkm_debug(subdev, "trying %s...\n", name ? name : func->name); + if (func->init) { + mthd->data = func->init(bios, name); + if (IS_ERR(mthd->data)) { + mthd->data = NULL; + return 0; + } + } + mthd->score = shadow_image(bios, 0, 0, mthd); + if (func->fini) + func->fini(mthd->data); + nvkm_debug(subdev, "scored %d\n", mthd->score); + mthd->data = bios->data; + mthd->size = bios->size; + bios->data = NULL; + bios->size = 0; + } + return mthd->score; +} + +static u32 +shadow_fw_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + const struct firmware *fw = data; + if (offset + length <= fw->size) { + memcpy(bios->data + offset, fw->data + offset, length); + return length; + } + return 0; +} + +static void * +shadow_fw_init(struct nvkm_bios *bios, const char *name) +{ + struct device *dev = bios->subdev.device->dev; + const struct firmware *fw; + int ret = request_firmware(&fw, name, dev); + if (ret) + return ERR_PTR(-ENOENT); + return (void *)fw; +} + +static const struct nvbios_source +shadow_fw = { + .name = "firmware", + .init = shadow_fw_init, + .fini = (void(*)(void *))release_firmware, + .read = shadow_fw_read, + .rw = false, +}; + +int +nvbios_shadow(struct nvkm_bios *bios) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvkm_device *device = subdev->device; + struct shadow mthds[] = { + { 0, &nvbios_of }, + { 0, &nvbios_ramin }, + { 0, &nvbios_prom }, + { 0, &nvbios_acpi_fast }, + { 4, &nvbios_acpi_slow }, + { 1, &nvbios_pcirom }, + { 1, &nvbios_platform }, + {} + }, *mthd, *best = NULL; + const char *optarg; + char *source; + int optlen; + + /* handle user-specified bios source */ + optarg = nvkm_stropt(device->cfgopt, "NvBios", &optlen); + source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL; + if (source) { + /* try to match one of the built-in methods */ + for (mthd = mthds; mthd->func; mthd++) { + if (mthd->func->name && + !strcasecmp(source, mthd->func->name)) { + best = mthd; + if (shadow_method(bios, mthd, NULL)) + break; + } + } + + /* otherwise, attempt to load as firmware */ + if (!best && (best = mthd)) { + mthd->func = &shadow_fw; + shadow_method(bios, mthd, source); + mthd->func = NULL; + } + + if (!best->score) { + nvkm_error(subdev, "%s invalid\n", source); + kfree(source); + source = NULL; + } + } + + /* scan all potential bios sources, looking for best image */ + if (!best || !best->score) { + for (mthd = mthds, best = mthd; mthd->func; mthd++) { + if (!mthd->skip || best->score < mthd->skip) { + if (shadow_method(bios, mthd, NULL)) { + if (mthd->score > best->score) + best = mthd; + } + } + } + } + + /* cleanup the ones we didn't use */ + for (mthd = mthds; mthd->func; mthd++) { + if (mthd != best) + kfree(mthd->data); + } + + if (!best->score) { + nvkm_error(subdev, "unable to locate usable image\n"); + return -EINVAL; + } + + nvkm_debug(subdev, "using image from %s\n", best->func ? + best->func->name : source); + bios->data = best->data; + bios->size = best->size; + kfree(source); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c new file mode 100644 index 000000000..f9c427559 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c @@ -0,0 +1,140 @@ +/* + * 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. + * + */ +#include "priv.h" + +static int +acpi_read_bios(acpi_handle rom_handle, u8 *bios, u32 offset, u32 length) +{ +#if defined(CONFIG_ACPI) && defined(CONFIG_X86) + acpi_status status; + union acpi_object rom_arg_elements[2], *obj; + struct acpi_object_list rom_arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; + + rom_arg.count = 2; + rom_arg.pointer = &rom_arg_elements[0]; + + rom_arg_elements[0].type = ACPI_TYPE_INTEGER; + rom_arg_elements[0].integer.value = offset; + + rom_arg_elements[1].type = ACPI_TYPE_INTEGER; + rom_arg_elements[1].integer.value = length; + + status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); + if (ACPI_FAILURE(status)) { + pr_info("failed to evaluate ROM got %s\n", + acpi_format_exception(status)); + return -ENODEV; + } + obj = (union acpi_object *)buffer.pointer; + length = min(length, obj->buffer.length); + memcpy(bios+offset, obj->buffer.pointer, length); + kfree(buffer.pointer); + return length; +#else + return -EINVAL; +#endif +} + +/* This version of the shadow function disobeys the ACPI spec and tries + * to fetch in units of more than 4KiB at a time. This is a LOT faster + * on some systems, such as Lenovo W530. + */ +static u32 +acpi_read_fast(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + u32 limit = (offset + length + 0xfff) & ~0xfff; + u32 start = offset & ~0x00000fff; + u32 fetch = limit - start; + + if (nvbios_extend(bios, limit) >= 0) { + int ret = acpi_read_bios(data, bios->data, start, fetch); + if (ret == fetch) + return fetch; + } + + return 0; +} + +/* Other systems, such as the one in fdo#55948, will report a success + * but only return 4KiB of data. The common bios fetching logic will + * detect an invalid image, and fall back to this version of the read + * function. + */ +static u32 +acpi_read_slow(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + u32 limit = (offset + length + 0xfff) & ~0xfff; + u32 start = offset & ~0xfff; + u32 fetch = 0; + + if (nvbios_extend(bios, limit) >= 0) { + while (start + fetch < limit) { + int ret = acpi_read_bios(data, bios->data, + start + fetch, 0x1000); + if (ret != 0x1000) + break; + fetch += 0x1000; + } + } + + return fetch; +} + +static void * +acpi_init(struct nvkm_bios *bios, const char *name) +{ +#if defined(CONFIG_ACPI) && defined(CONFIG_X86) + acpi_status status; + acpi_handle dhandle, rom_handle; + + dhandle = ACPI_HANDLE(bios->subdev.device->dev); + if (!dhandle) + return ERR_PTR(-ENODEV); + + status = acpi_get_handle(dhandle, "_ROM", &rom_handle); + if (ACPI_FAILURE(status)) + return ERR_PTR(-ENODEV); + + return rom_handle; +#else + return ERR_PTR(-ENODEV); +#endif +} + +const struct nvbios_source +nvbios_acpi_fast = { + .name = "ACPI", + .init = acpi_init, + .read = acpi_read_fast, + .rw = false, + .require_checksum = true, +}; + +const struct nvbios_source +nvbios_acpi_slow = { + .name = "ACPI", + .init = acpi_init, + .read = acpi_read_slow, + .rw = false, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c new file mode 100644 index 000000000..4bf486b57 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c @@ -0,0 +1,84 @@ +/* + * 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. + * + */ +#include "priv.h" + +#include + +#if defined(__powerpc__) +struct priv { + const void __iomem *data; + int size; +}; + +static u32 +of_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + struct priv *priv = data; + if (offset < priv->size) { + length = min_t(u32, length, priv->size - offset); + memcpy_fromio(bios->data + offset, priv->data + offset, length); + return length; + } + return 0; +} + +static u32 +of_size(void *data) +{ + struct priv *priv = data; + return priv->size; +} + +static void * +of_init(struct nvkm_bios *bios, const char *name) +{ + struct nvkm_device *device = bios->subdev.device; + struct pci_dev *pdev = device->func->pci(device)->pdev; + struct device_node *dn; + struct priv *priv; + if (!(dn = pci_device_to_OF_node(pdev))) + return ERR_PTR(-ENODEV); + if (!(priv = kzalloc(sizeof(*priv), GFP_KERNEL))) + return ERR_PTR(-ENOMEM); + if ((priv->data = of_get_property(dn, "NVDA,BMP", &priv->size))) + return priv; + kfree(priv); + return ERR_PTR(-EINVAL); +} + +const struct nvbios_source +nvbios_of = { + .name = "OpenFirmware", + .init = of_init, + .fini = (void(*)(void *))kfree, + .read = of_read, + .size = of_size, + .rw = false, + .ignore_checksum = true, + .no_pcir = true, +}; +#else +const struct nvbios_source +nvbios_of = { +}; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c new file mode 100644 index 000000000..8d9812a51 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c @@ -0,0 +1,134 @@ +/* + * 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. + * + */ +#include "priv.h" + +#include + +struct priv { + struct pci_dev *pdev; + void __iomem *rom; + size_t size; +}; + +static u32 +pcirom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + struct priv *priv = data; + if (offset + length <= priv->size) { + memcpy_fromio(bios->data + offset, priv->rom + offset, length); + return length; + } + return 0; +} + +static void +pcirom_fini(void *data) +{ + struct priv *priv = data; + pci_unmap_rom(priv->pdev, priv->rom); + pci_disable_rom(priv->pdev); + kfree(priv); +} + +static void * +pcirom_init(struct nvkm_bios *bios, const char *name) +{ + struct nvkm_device *device = bios->subdev.device; + struct priv *priv = NULL; + struct pci_dev *pdev; + int ret; + + if (device->func->pci) + pdev = device->func->pci(device)->pdev; + else + return ERR_PTR(-ENODEV); + + if (!(ret = pci_enable_rom(pdev))) { + if (ret = -ENOMEM, + (priv = kmalloc(sizeof(*priv), GFP_KERNEL))) { + if (ret = -EFAULT, + (priv->rom = pci_map_rom(pdev, &priv->size))) { + priv->pdev = pdev; + return priv; + } + kfree(priv); + } + pci_disable_rom(pdev); + } + + return ERR_PTR(ret); +} + +const struct nvbios_source +nvbios_pcirom = { + .name = "PCIROM", + .init = pcirom_init, + .fini = pcirom_fini, + .read = pcirom_read, + .rw = true, +}; + +static void * +platform_init(struct nvkm_bios *bios, const char *name) +{ + struct nvkm_device *device = bios->subdev.device; + struct pci_dev *pdev; + struct priv *priv; + int ret = -ENOMEM; + + if (device->func->pci) + pdev = device->func->pci(device)->pdev; + else + return ERR_PTR(-ENODEV); + + if (!pdev->rom || pdev->romlen == 0) + return ERR_PTR(-ENODEV); + + if ((priv = kmalloc(sizeof(*priv), GFP_KERNEL))) { + priv->size = pdev->romlen; + if (ret = -ENODEV, + (priv->rom = ioremap(pdev->rom, pdev->romlen))) + return priv; + kfree(priv); + } + + return ERR_PTR(ret); +} + +static void +platform_fini(void *data) +{ + struct priv *priv = data; + + iounmap(priv->rom); + kfree(priv); +} + +const struct nvbios_source +nvbios_platform = { + .name = "PLATFORM", + .init = platform_init, + .fini = platform_fini, + .read = pcirom_read, + .rw = true, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c new file mode 100644 index 000000000..023ddc7c5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c @@ -0,0 +1,123 @@ +/* + * 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. + * + */ +#include "priv.h" + +struct priv { + struct nvkm_bios *bios; + u32 bar0; +}; + +static u32 +pramin_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + struct nvkm_device *device = bios->subdev.device; + u32 i; + if (offset + length <= 0x00100000) { + for (i = offset; i < offset + length; i += 4) + *(u32 *)&bios->data[i] = nvkm_rd32(device, 0x700000 + i); + return length; + } + return 0; +} + +static void +pramin_fini(void *data) +{ + struct priv *priv = data; + if (priv) { + struct nvkm_device *device = priv->bios->subdev.device; + nvkm_wr32(device, 0x001700, priv->bar0); + kfree(priv); + } +} + +static void * +pramin_init(struct nvkm_bios *bios, const char *name) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvkm_device *device = subdev->device; + struct priv *priv = NULL; + u64 addr = 0; + + /* PRAMIN always potentially available prior to nv50 */ + if (device->card_type < NV_50) + return NULL; + + /* we can't get the bios image pointer without PDISP */ + if (device->card_type >= GA100) + addr = device->chipset == 0x170; /*XXX: find the fuse reg for this */ + else + if (device->card_type >= GM100) + addr = nvkm_rd32(device, 0x021c04); + else + if (device->card_type >= NV_C0) + addr = nvkm_rd32(device, 0x022500); + if (addr & 0x00000001) { + nvkm_debug(subdev, "... display disabled\n"); + return ERR_PTR(-ENODEV); + } + + /* check that the window is enabled and in vram, particularly + * important as we don't want to be touching vram on an + * uninitialised board + */ + if (device->card_type >= GV100) + addr = nvkm_rd32(device, 0x625f04); + else + addr = nvkm_rd32(device, 0x619f04); + if (!(addr & 0x00000008)) { + nvkm_debug(subdev, "... not enabled\n"); + return ERR_PTR(-ENODEV); + } + if ( (addr & 0x00000003) != 1) { + nvkm_debug(subdev, "... not in vram\n"); + return ERR_PTR(-ENODEV); + } + + /* some alternate method inherited from xf86-video-nv... */ + addr = (addr & 0xffffff00) << 8; + if (!addr) { + addr = (u64)nvkm_rd32(device, 0x001700) << 16; + addr += 0xf0000; + } + + /* modify bar0 PRAMIN window to cover the bios image */ + if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL))) { + nvkm_error(subdev, "... out of memory\n"); + return ERR_PTR(-ENOMEM); + } + + priv->bios = bios; + priv->bar0 = nvkm_rd32(device, 0x001700); + nvkm_wr32(device, 0x001700, addr >> 16); + return priv; +} + +const struct nvbios_source +nvbios_ramin = { + .name = "PRAMIN", + .init = pramin_init, + .fini = pramin_fini, + .read = pramin_read, + .rw = true, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c new file mode 100644 index 000000000..39144ceb1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c @@ -0,0 +1,64 @@ +/* + * 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. + * + */ +#include "priv.h" + +#include + +static u32 +nvbios_prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) +{ + struct nvkm_device *device = data; + u32 i; + if (offset + length <= 0x00100000) { + for (i = offset; i < offset + length; i += 4) + *(u32 *)&bios->data[i] = nvkm_rd32(device, 0x300000 + i); + return length; + } + return 0; +} + +static void +nvbios_prom_fini(void *data) +{ + struct nvkm_device *device = data; + nvkm_pci_rom_shadow(device->pci, true); +} + +static void * +nvbios_prom_init(struct nvkm_bios *bios, const char *name) +{ + struct nvkm_device *device = bios->subdev.device; + if (device->card_type == NV_40 && device->chipset >= 0x4c) + return ERR_PTR(-ENODEV); + nvkm_pci_rom_shadow(device->pci, false); + return device; +} + +const struct nvbios_source +nvbios_prom = { + .name = "PROM", + .init = nvbios_prom_init, + .fini = nvbios_prom_fini, + .read = nvbios_prom_read, + .rw = false, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c new file mode 100644 index 000000000..5babc5a7c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c @@ -0,0 +1,212 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + */ +#include +#include +#include + +static u32 +therm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) +{ + struct bit_entry bit_P; + u32 therm = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 1) + therm = nvbios_rd32(bios, bit_P.offset + 12); + else if (bit_P.version == 2) + therm = nvbios_rd32(bios, bit_P.offset + 16); + else + nvkm_error(&bios->subdev, + "unknown offset for thermal in BIT P %d\n", + bit_P.version); + } + + /* exit now if we haven't found the thermal table */ + if (!therm) + return 0; + + *ver = nvbios_rd08(bios, therm + 0); + *hdr = nvbios_rd08(bios, therm + 1); + *len = nvbios_rd08(bios, therm + 2); + *cnt = nvbios_rd08(bios, therm + 3); + return therm + nvbios_rd08(bios, therm + 1); +} + +static u32 +nvbios_therm_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u32 therm = therm_table(bios, ver, &hdr, len, &cnt); + if (therm && idx < cnt) + return therm + idx * *len; + return 0; +} + +int +nvbios_therm_sensor_parse(struct nvkm_bios *bios, + enum nvbios_therm_domain domain, + struct nvbios_therm_sensor *sensor) +{ + s8 thrs_section, sensor_section, offset; + u8 ver, len, i; + u32 entry; + + /* we only support the core domain for now */ + if (domain != NVBIOS_THERM_DOMAIN_CORE) + return -EINVAL; + + /* Read the entries from the table */ + thrs_section = 0; + sensor_section = -1; + i = 0; + while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) { + s16 value = nvbios_rd16(bios, entry + 1); + + switch (nvbios_rd08(bios, entry + 0)) { + case 0x0: + thrs_section = value; + if (value > 0) + return 0; /* we do not try to support ambient */ + break; + case 0x01: + sensor_section++; + if (sensor_section == 0) { + offset = ((s8) nvbios_rd08(bios, entry + 2)) / 2; + sensor->offset_constant = offset; + } + break; + + case 0x04: + if (thrs_section == 0) { + sensor->thrs_critical.temp = (value & 0xff0) >> 4; + sensor->thrs_critical.hysteresis = value & 0xf; + } + break; + + case 0x07: + if (thrs_section == 0) { + sensor->thrs_down_clock.temp = (value & 0xff0) >> 4; + sensor->thrs_down_clock.hysteresis = value & 0xf; + } + break; + + case 0x08: + if (thrs_section == 0) { + sensor->thrs_fan_boost.temp = (value & 0xff0) >> 4; + sensor->thrs_fan_boost.hysteresis = value & 0xf; + } + break; + + case 0x10: + if (sensor_section == 0) + sensor->offset_num = value; + break; + + case 0x11: + if (sensor_section == 0) + sensor->offset_den = value; + break; + + case 0x12: + if (sensor_section == 0) + sensor->slope_mult = value; + break; + + case 0x13: + if (sensor_section == 0) + sensor->slope_div = value; + break; + case 0x32: + if (thrs_section == 0) { + sensor->thrs_shutdown.temp = (value & 0xff0) >> 4; + sensor->thrs_shutdown.hysteresis = value & 0xf; + } + break; + } + } + + return 0; +} + +int +nvbios_therm_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan) +{ + struct nvbios_therm_trip_point *cur_trip = NULL; + u8 ver, len, i; + u32 entry; + + uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0, + 75, 0, 85, 0, 100, 0, 100, 0 }; + + i = 0; + fan->nr_fan_trip = 0; + fan->fan_mode = NVBIOS_THERM_FAN_OTHER; + while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) { + s16 value = nvbios_rd16(bios, entry + 1); + + switch (nvbios_rd08(bios, entry + 0)) { + case 0x22: + fan->min_duty = value & 0xff; + fan->max_duty = (value & 0xff00) >> 8; + break; + case 0x24: + fan->nr_fan_trip++; + if (fan->fan_mode > NVBIOS_THERM_FAN_TRIP) + fan->fan_mode = NVBIOS_THERM_FAN_TRIP; + cur_trip = &fan->trip[fan->nr_fan_trip - 1]; + cur_trip->hysteresis = value & 0xf; + cur_trip->temp = (value & 0xff0) >> 4; + cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12]; + break; + case 0x25: + cur_trip = &fan->trip[fan->nr_fan_trip - 1]; + cur_trip->fan_duty = value; + break; + case 0x26: + if (!fan->pwm_freq) + fan->pwm_freq = value; + break; + case 0x3b: + fan->bump_period = value; + break; + case 0x3c: + fan->slow_down_period = value; + break; + case 0x46: + if (fan->fan_mode > NVBIOS_THERM_FAN_LINEAR) + fan->fan_mode = NVBIOS_THERM_FAN_LINEAR; + fan->linear_min_temp = nvbios_rd08(bios, entry + 1); + fan->linear_max_temp = nvbios_rd08(bios, entry + 2); + break; + } + } + + /* starting from fermi, fan management is always linear */ + if (bios->subdev.device->card_type >= NV_C0 && + fan->fan_mode == NVBIOS_THERM_FAN_OTHER) { + fan->fan_mode = NVBIOS_THERM_FAN_LINEAR; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c new file mode 100644 index 000000000..2da45e29f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c @@ -0,0 +1,173 @@ +/* + * 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 +#include +#include + +u32 +nvbios_timingTe(struct nvkm_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_P; + u32 timing = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 1) + timing = nvbios_rd32(bios, bit_P.offset + 4); + else + if (bit_P.version == 2) + timing = nvbios_rd32(bios, bit_P.offset + 8); + + if (timing) { + *ver = nvbios_rd08(bios, timing + 0); + switch (*ver) { + case 0x10: + *hdr = nvbios_rd08(bios, timing + 1); + *cnt = nvbios_rd08(bios, timing + 2); + *len = nvbios_rd08(bios, timing + 3); + *snr = 0; + *ssz = 0; + return timing; + case 0x20: + *hdr = nvbios_rd08(bios, timing + 1); + *cnt = nvbios_rd08(bios, timing + 5); + *len = nvbios_rd08(bios, timing + 2); + *snr = nvbios_rd08(bios, timing + 4); + *ssz = nvbios_rd08(bios, timing + 3); + return timing; + default: + break; + } + } + } + + return 0; +} + +u32 +nvbios_timingEe(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 timing = nvbios_timingTe(bios, ver, hdr, cnt, len, &snr, &ssz); + if (timing && idx < *cnt) { + timing += *hdr + idx * (*len + (snr * ssz)); + *hdr = *len; + *cnt = snr; + *len = ssz; + return timing; + } + return 0; +} + +u32 +nvbios_timingEp(struct nvkm_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p) +{ + u32 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp; + p->timing_ver = *ver; + p->timing_hdr = *hdr; + switch (!!data * *ver) { + case 0x10: + p->timing_10_WR = nvbios_rd08(bios, data + 0x00); + p->timing_10_WTR = nvbios_rd08(bios, data + 0x01); + p->timing_10_CL = nvbios_rd08(bios, data + 0x02); + p->timing_10_RC = nvbios_rd08(bios, data + 0x03); + p->timing_10_RFC = nvbios_rd08(bios, data + 0x05); + p->timing_10_RAS = nvbios_rd08(bios, data + 0x07); + p->timing_10_RP = nvbios_rd08(bios, data + 0x09); + p->timing_10_RCDRD = nvbios_rd08(bios, data + 0x0a); + p->timing_10_RCDWR = nvbios_rd08(bios, data + 0x0b); + p->timing_10_RRD = nvbios_rd08(bios, data + 0x0c); + p->timing_10_13 = nvbios_rd08(bios, data + 0x0d); + p->timing_10_ODT = nvbios_rd08(bios, data + 0x0e) & 0x07; + if (p->ramcfg_ver >= 0x10) + p->ramcfg_RON = nvbios_rd08(bios, data + 0x0e) & 0x07; + + p->timing_10_24 = 0xff; + p->timing_10_21 = 0; + p->timing_10_20 = 0; + p->timing_10_CWL = 0; + p->timing_10_18 = 0; + p->timing_10_16 = 0; + + switch (min_t(u8, *hdr, 25)) { + case 25: + p->timing_10_24 = nvbios_rd08(bios, data + 0x18); + fallthrough; + case 24: + case 23: + case 22: + p->timing_10_21 = nvbios_rd08(bios, data + 0x15); + fallthrough; + case 21: + p->timing_10_20 = nvbios_rd08(bios, data + 0x14); + fallthrough; + case 20: + p->timing_10_CWL = nvbios_rd08(bios, data + 0x13); + fallthrough; + case 19: + p->timing_10_18 = nvbios_rd08(bios, data + 0x12); + fallthrough; + case 18: + case 17: + p->timing_10_16 = nvbios_rd08(bios, data + 0x10); + } + + break; + case 0x20: + p->timing[0] = nvbios_rd32(bios, data + 0x00); + p->timing[1] = nvbios_rd32(bios, data + 0x04); + p->timing[2] = nvbios_rd32(bios, data + 0x08); + p->timing[3] = nvbios_rd32(bios, data + 0x0c); + p->timing[4] = nvbios_rd32(bios, data + 0x10); + p->timing[5] = nvbios_rd32(bios, data + 0x14); + p->timing[6] = nvbios_rd32(bios, data + 0x18); + p->timing[7] = nvbios_rd32(bios, data + 0x1c); + p->timing[8] = nvbios_rd32(bios, data + 0x20); + p->timing[9] = nvbios_rd32(bios, data + 0x24); + p->timing[10] = nvbios_rd32(bios, data + 0x28); + p->timing_20_2e_03 = (nvbios_rd08(bios, data + 0x2e) & 0x03) >> 0; + p->timing_20_2e_30 = (nvbios_rd08(bios, data + 0x2e) & 0x30) >> 4; + p->timing_20_2e_c0 = (nvbios_rd08(bios, data + 0x2e) & 0xc0) >> 6; + p->timing_20_2f_03 = (nvbios_rd08(bios, data + 0x2f) & 0x03) >> 0; + temp = nvbios_rd16(bios, data + 0x2c); + p->timing_20_2c_003f = (temp & 0x003f) >> 0; + p->timing_20_2c_1fc0 = (temp & 0x1fc0) >> 6; + p->timing_20_30_07 = (nvbios_rd08(bios, data + 0x30) & 0x07) >> 0; + p->timing_20_30_f8 = (nvbios_rd08(bios, data + 0x30) & 0xf8) >> 3; + temp = nvbios_rd16(bios, data + 0x31); + p->timing_20_31_0007 = (temp & 0x0007) >> 0; + p->timing_20_31_0078 = (temp & 0x0078) >> 3; + p->timing_20_31_0780 = (temp & 0x0780) >> 7; + p->timing_20_31_0800 = (temp & 0x0800) >> 11; + p->timing_20_31_7000 = (temp & 0x7000) >> 12; + p->timing_20_31_8000 = (temp & 0x8000) >> 15; + break; + default: + data = 0; + break; + } + return data; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c new file mode 100644 index 000000000..c228ca15f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c @@ -0,0 +1,121 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + */ +#include +#include +#include + +u32 +nvbios_vmap_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_P; + u32 vmap = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) { + vmap = nvbios_rd32(bios, bit_P.offset + 0x20); + if (vmap) { + *ver = nvbios_rd08(bios, vmap + 0); + switch (*ver) { + case 0x10: + case 0x20: + *hdr = nvbios_rd08(bios, vmap + 1); + *cnt = nvbios_rd08(bios, vmap + 3); + *len = nvbios_rd08(bios, vmap + 2); + return vmap; + default: + break; + } + } + } + } + + return 0; +} + +u32 +nvbios_vmap_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_vmap *info) +{ + u32 vmap = nvbios_vmap_table(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!vmap * *ver) { + case 0x10: + info->max0 = 0xff; + info->max1 = 0xff; + info->max2 = 0xff; + break; + case 0x20: + info->max0 = nvbios_rd08(bios, vmap + 0x7); + info->max1 = nvbios_rd08(bios, vmap + 0x8); + if (*len >= 0xc) + info->max2 = nvbios_rd08(bios, vmap + 0xc); + else + info->max2 = 0xff; + break; + } + return vmap; +} + +u32 +nvbios_vmap_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u32 vmap = nvbios_vmap_table(bios, ver, &hdr, &cnt, len); + if (vmap && idx < cnt) { + vmap = vmap + hdr + (idx * *len); + return vmap; + } + return 0; +} + +u32 +nvbios_vmap_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, + struct nvbios_vmap_entry *info) +{ + u32 vmap = nvbios_vmap_entry(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!vmap * *ver) { + case 0x10: + info->link = 0xff; + info->min = nvbios_rd32(bios, vmap + 0x00); + info->max = nvbios_rd32(bios, vmap + 0x04); + info->arg[0] = nvbios_rd32(bios, vmap + 0x08); + info->arg[1] = nvbios_rd32(bios, vmap + 0x0c); + info->arg[2] = nvbios_rd32(bios, vmap + 0x10); + break; + case 0x20: + info->mode = nvbios_rd08(bios, vmap + 0x00); + info->link = nvbios_rd08(bios, vmap + 0x01); + info->min = nvbios_rd32(bios, vmap + 0x02); + info->max = nvbios_rd32(bios, vmap + 0x06); + info->arg[0] = nvbios_rd32(bios, vmap + 0x0a); + info->arg[1] = nvbios_rd32(bios, vmap + 0x0e); + info->arg[2] = nvbios_rd32(bios, vmap + 0x12); + info->arg[3] = nvbios_rd32(bios, vmap + 0x16); + info->arg[4] = nvbios_rd32(bios, vmap + 0x1a); + info->arg[5] = nvbios_rd32(bios, vmap + 0x1e); + break; + } + return vmap; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c new file mode 100644 index 000000000..33a9fb5ac --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c @@ -0,0 +1,160 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + */ +#include +#include +#include + +u32 +nvbios_volt_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_P; + u32 volt = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2) + volt = nvbios_rd32(bios, bit_P.offset + 0x0c); + else + if (bit_P.version == 1) + volt = nvbios_rd32(bios, bit_P.offset + 0x10); + + if (volt) { + *ver = nvbios_rd08(bios, volt + 0); + switch (*ver) { + case 0x12: + *hdr = 5; + *cnt = nvbios_rd08(bios, volt + 2); + *len = nvbios_rd08(bios, volt + 1); + return volt; + case 0x20: + *hdr = nvbios_rd08(bios, volt + 1); + *cnt = nvbios_rd08(bios, volt + 2); + *len = nvbios_rd08(bios, volt + 3); + return volt; + case 0x30: + case 0x40: + case 0x50: + *hdr = nvbios_rd08(bios, volt + 1); + *cnt = nvbios_rd08(bios, volt + 3); + *len = nvbios_rd08(bios, volt + 2); + return volt; + } + } + } + + return 0; +} + +u32 +nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_volt *info) +{ + u32 volt = nvbios_volt_table(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!volt * *ver) { + case 0x12: + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x04); + info->ranged = false; + break; + case 0x20: + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x05); + info->ranged = false; + break; + case 0x30: + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x04); + info->ranged = false; + break; + case 0x40: + info->type = NVBIOS_VOLT_GPIO; + info->base = nvbios_rd32(bios, volt + 0x04); + info->step = nvbios_rd16(bios, volt + 0x08); + info->vidmask = nvbios_rd08(bios, volt + 0x0b); + info->ranged = true; /* XXX: find the flag byte */ + info->min = min(info->base, + info->base + info->step * info->vidmask); + info->max = nvbios_rd32(bios, volt + 0x0e); + if (!info->max) + info->max = max(info->base, info->base + info->step * info->vidmask); + break; + case 0x50: + info->min = nvbios_rd32(bios, volt + 0x0a); + info->max = nvbios_rd32(bios, volt + 0x0e); + info->base = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff; + + /* offset 4 seems to be a flag byte */ + if (nvbios_rd32(bios, volt + 0x4) & 1) { + info->type = NVBIOS_VOLT_PWM; + info->pwm_freq = nvbios_rd32(bios, volt + 0x5) / 1000; + info->pwm_range = nvbios_rd32(bios, volt + 0x16); + } else { + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x06); + info->step = nvbios_rd16(bios, volt + 0x16); + info->ranged = + !!(nvbios_rd08(bios, volt + 0x4) & 0x2); + } + break; + } + return volt; +} + +u32 +nvbios_volt_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u32 volt = nvbios_volt_table(bios, ver, &hdr, &cnt, len); + if (volt && idx < cnt) { + volt = volt + hdr + (idx * *len); + return volt; + } + return 0; +} + +u32 +nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, + struct nvbios_volt_entry *info) +{ + u32 volt = nvbios_volt_entry(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!volt * *ver) { + case 0x12: + case 0x20: + info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000; + info->vid = nvbios_rd08(bios, volt + 0x01); + break; + case 0x30: + info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000; + info->vid = nvbios_rd08(bios, volt + 0x01) >> 2; + break; + case 0x40: + break; + case 0x50: + info->voltage = nvbios_rd32(bios, volt) & 0x001fffff; + info->vid = (nvbios_rd32(bios, volt) >> 23) & 0xff; + break; + } + return volt; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c new file mode 100644 index 000000000..71524548d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vpstate.c @@ -0,0 +1,88 @@ +/* + * Copyright 2016 Karol Herbst + * + * 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: Karol Herbst + */ +#include +#include +#include + +static u32 +nvbios_vpstate_offset(struct nvkm_bios *b) +{ + struct bit_entry bit_P; + + if (!bit_entry(b, 'P', &bit_P)) { + if (bit_P.version == 2 && bit_P.length >= 0x3c) + return nvbios_rd32(b, bit_P.offset + 0x38); + } + + return 0x0000; +} + +int +nvbios_vpstate_parse(struct nvkm_bios *b, struct nvbios_vpstate_header *h) +{ + if (!h) + return -EINVAL; + + h->offset = nvbios_vpstate_offset(b); + if (!h->offset) + return -ENODEV; + + h->version = nvbios_rd08(b, h->offset); + switch (h->version) { + case 0x10: + h->hlen = nvbios_rd08(b, h->offset + 0x1); + h->elen = nvbios_rd08(b, h->offset + 0x2); + h->slen = nvbios_rd08(b, h->offset + 0x3); + h->scount = nvbios_rd08(b, h->offset + 0x4); + h->ecount = nvbios_rd08(b, h->offset + 0x5); + + h->base_id = nvbios_rd08(b, h->offset + 0x0f); + if (h->hlen > 0x10) + h->boost_id = nvbios_rd08(b, h->offset + 0x10); + else + h->boost_id = 0xff; + if (h->hlen > 0x11) + h->tdp_id = nvbios_rd08(b, h->offset + 0x11); + else + h->tdp_id = 0xff; + return 0; + default: + return -EINVAL; + } +} + +int +nvbios_vpstate_entry(struct nvkm_bios *b, struct nvbios_vpstate_header *h, + u8 idx, struct nvbios_vpstate_entry *e) +{ + u32 offset; + + if (!e || !h || idx > h->ecount) + return -EINVAL; + + offset = h->offset + h->hlen + idx * (h->elen + (h->slen * h->scount)); + e->pstate = nvbios_rd08(b, offset); + e->clock_mhz = nvbios_rd16(b, offset + 0x5); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c new file mode 100644 index 000000000..250fc42d8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c @@ -0,0 +1,74 @@ +/* + * 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 +#include +#include + +static u16 +dcb_xpiod_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len); + if (data && *ver >= 0x40 && *hdr >= 0x06) { + u16 xpio = nvbios_rd16(bios, data + 0x04); + if (xpio) { + *ver = nvbios_rd08(bios, data + 0x00); + *hdr = nvbios_rd08(bios, data + 0x01); + *cnt = nvbios_rd08(bios, data + 0x02); + *len = nvbios_rd08(bios, data + 0x03); + return xpio; + } + } + return 0x0000; +} + +u16 +dcb_xpio_table(struct nvkm_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 data = dcb_xpiod_table(bios, ver, hdr, cnt, len); + if (data && idx < *cnt) { + u16 xpio = nvbios_rd16(bios, data + *hdr + (idx * *len)); + if (xpio) { + *ver = nvbios_rd08(bios, data + 0x00); + *hdr = nvbios_rd08(bios, data + 0x01); + *cnt = nvbios_rd08(bios, data + 0x02); + *len = nvbios_rd08(bios, data + 0x03); + return xpio; + } + } + return 0x0000; +} + +u16 +dcb_xpio_parse(struct nvkm_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *info) +{ + u16 data = dcb_xpio_table(bios, idx, ver, hdr, cnt, len); + if (data && *len >= 6) { + info->type = nvbios_rd08(bios, data + 0x04); + info->addr = nvbios_rd08(bios, data + 0x05); + info->flags = nvbios_rd08(bios, data + 0x06); + } + return 0x0000; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild new file mode 100644 index 000000000..01d737989 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/bus/base.o +nvkm-y += nvkm/subdev/bus/hwsq.o +nvkm-y += nvkm/subdev/bus/nv04.o +nvkm-y += nvkm/subdev/bus/nv31.o +nvkm-y += nvkm/subdev/bus/nv50.o +nvkm-y += nvkm/subdev/bus/g94.o +nvkm-y += nvkm/subdev/bus/gf100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/base.c new file mode 100644 index 000000000..0e5a46db5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/base.c @@ -0,0 +1,64 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static void +nvkm_bus_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_bus *bus = nvkm_bus(subdev); + bus->func->intr(bus); +} + +static int +nvkm_bus_init(struct nvkm_subdev *subdev) +{ + struct nvkm_bus *bus = nvkm_bus(subdev); + bus->func->init(bus); + return 0; +} + +static void * +nvkm_bus_dtor(struct nvkm_subdev *subdev) +{ + return nvkm_bus(subdev); +} + +static const struct nvkm_subdev_func +nvkm_bus = { + .dtor = nvkm_bus_dtor, + .init = nvkm_bus_init, + .intr = nvkm_bus_intr, +}; + +int +nvkm_bus_new_(const struct nvkm_bus_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_bus **pbus) +{ + struct nvkm_bus *bus; + if (!(bus = *pbus = kzalloc(sizeof(*bus), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_bus, device, type, inst, &bus->subdev); + bus->func = func; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c new file mode 100644 index 000000000..a0d6e2d3f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c @@ -0,0 +1,65 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + * Ben Skeggs + */ +#include "priv.h" + +#include + +static int +g94_bus_hwsq_exec(struct nvkm_bus *bus, u32 *data, u32 size) +{ + struct nvkm_device *device = bus->subdev.device; + int i; + + nvkm_mask(device, 0x001098, 0x00000008, 0x00000000); + nvkm_wr32(device, 0x001304, 0x00000000); + nvkm_wr32(device, 0x001318, 0x00000000); + for (i = 0; i < size; i++) + nvkm_wr32(device, 0x080000 + (i * 4), data[i]); + nvkm_mask(device, 0x001098, 0x00000018, 0x00000018); + nvkm_wr32(device, 0x00130c, 0x00000001); + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x001308) & 0x00000100)) + break; + ) < 0) + return -ETIMEDOUT; + + return 0; +} + +static const struct nvkm_bus_func +g94_bus = { + .init = nv50_bus_init, + .intr = nv50_bus_intr, + .hwsq_exec = g94_bus_hwsq_exec, + .hwsq_size = 128, +}; + +int +g94_bus_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bus **pbus) +{ + return nvkm_bus_new_(&g94_bus, device, type, inst, pbus); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c new file mode 100644 index 000000000..80b5aacee --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c @@ -0,0 +1,76 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + * Ben Skeggs + */ +#include "priv.h" + +static void +gf100_bus_intr(struct nvkm_bus *bus) +{ + struct nvkm_subdev *subdev = &bus->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x001100) & nvkm_rd32(device, 0x001140); + + if (stat & 0x0000000e) { + u32 addr = nvkm_rd32(device, 0x009084); + u32 data = nvkm_rd32(device, 0x009088); + + nvkm_error_ratelimited(subdev, + "MMIO %s of %08x FAULT at %06x [ %s%s%s]\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc), + (stat & 0x00000002) ? "!ENGINE " : "", + (stat & 0x00000004) ? "PRIVRING " : "", + (stat & 0x00000008) ? "TIMEOUT " : ""); + + nvkm_wr32(device, 0x009084, 0x00000000); + nvkm_wr32(device, 0x001100, (stat & 0x0000000e)); + stat &= ~0x0000000e; + } + + if (stat) { + nvkm_error(subdev, "intr %08x\n", stat); + nvkm_mask(device, 0x001140, stat, 0x00000000); + } +} + +static void +gf100_bus_init(struct nvkm_bus *bus) +{ + struct nvkm_device *device = bus->subdev.device; + nvkm_wr32(device, 0x001100, 0xffffffff); + nvkm_wr32(device, 0x001140, 0x0000000e); +} + +static const struct nvkm_bus_func +gf100_bus = { + .init = gf100_bus_init, + .intr = gf100_bus_intr, +}; + +int +gf100_bus_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bus **pbus) +{ + return nvkm_bus_new_(&gf100_bus, device, type, inst, pbus); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c new file mode 100644 index 000000000..2a5668938 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c @@ -0,0 +1,177 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +struct nvkm_hwsq { + struct nvkm_subdev *subdev; + u32 addr; + u32 data; + struct { + u8 data[512]; + u16 size; + } c; +}; + +static void +hwsq_cmd(struct nvkm_hwsq *hwsq, int size, u8 data[]) +{ + memcpy(&hwsq->c.data[hwsq->c.size], data, size * sizeof(data[0])); + hwsq->c.size += size; +} + +int +nvkm_hwsq_init(struct nvkm_subdev *subdev, struct nvkm_hwsq **phwsq) +{ + struct nvkm_hwsq *hwsq; + + hwsq = *phwsq = kmalloc(sizeof(*hwsq), GFP_KERNEL); + if (hwsq) { + hwsq->subdev = subdev; + hwsq->addr = ~0; + hwsq->data = ~0; + memset(hwsq->c.data, 0x7f, sizeof(hwsq->c.data)); + hwsq->c.size = 0; + } + + return hwsq ? 0 : -ENOMEM; +} + +int +nvkm_hwsq_fini(struct nvkm_hwsq **phwsq, bool exec) +{ + struct nvkm_hwsq *hwsq = *phwsq; + int ret = 0, i; + if (hwsq) { + struct nvkm_subdev *subdev = hwsq->subdev; + struct nvkm_bus *bus = subdev->device->bus; + hwsq->c.size = (hwsq->c.size + 4) / 4; + if (hwsq->c.size <= bus->func->hwsq_size) { + if (exec) + ret = bus->func->hwsq_exec(bus, + (u32 *)hwsq->c.data, + hwsq->c.size); + if (ret) + nvkm_error(subdev, "hwsq exec failed: %d\n", ret); + } else { + nvkm_error(subdev, "hwsq ucode too large\n"); + ret = -ENOSPC; + } + + for (i = 0; ret && i < hwsq->c.size; i++) + nvkm_error(subdev, "\t%08x\n", ((u32 *)hwsq->c.data)[i]); + + *phwsq = NULL; + kfree(hwsq); + } + return ret; +} + +void +nvkm_hwsq_wr32(struct nvkm_hwsq *hwsq, u32 addr, u32 data) +{ + nvkm_debug(hwsq->subdev, "R[%06x] = %08x\n", addr, data); + + if (hwsq->data != data) { + if ((data & 0xffff0000) != (hwsq->data & 0xffff0000)) { + hwsq_cmd(hwsq, 5, (u8[]){ 0xe2, data, data >> 8, + data >> 16, data >> 24 }); + } else { + hwsq_cmd(hwsq, 3, (u8[]){ 0x42, data, data >> 8 }); + } + } + + if ((addr & 0xffff0000) != (hwsq->addr & 0xffff0000)) { + hwsq_cmd(hwsq, 5, (u8[]){ 0xe0, addr, addr >> 8, + addr >> 16, addr >> 24 }); + } else { + hwsq_cmd(hwsq, 3, (u8[]){ 0x40, addr, addr >> 8 }); + } + + hwsq->addr = addr; + hwsq->data = data; +} + +void +nvkm_hwsq_setf(struct nvkm_hwsq *hwsq, u8 flag, int data) +{ + nvkm_debug(hwsq->subdev, " FLAG[%02x] = %d\n", flag, data); + flag += 0x80; + if (data >= 0) + flag += 0x20; + if (data >= 1) + flag += 0x20; + hwsq_cmd(hwsq, 1, (u8[]){ flag }); +} + +void +nvkm_hwsq_wait(struct nvkm_hwsq *hwsq, u8 flag, u8 data) +{ + nvkm_debug(hwsq->subdev, " WAIT[%02x] = %d\n", flag, data); + hwsq_cmd(hwsq, 3, (u8[]){ 0x5f, flag, data }); +} + +void +nvkm_hwsq_wait_vblank(struct nvkm_hwsq *hwsq) +{ + struct nvkm_subdev *subdev = hwsq->subdev; + struct nvkm_device *device = subdev->device; + u32 heads, x, y, px = 0; + int i, head_sync; + + heads = nvkm_rd32(device, 0x610050); + for (i = 0; i < 2; i++) { + /* Heuristic: sync to head with biggest resolution */ + if (heads & (2 << (i << 3))) { + x = nvkm_rd32(device, 0x610b40 + (0x540 * i)); + y = (x & 0xffff0000) >> 16; + x &= 0x0000ffff; + if ((x * y) > px) { + px = (x * y); + head_sync = i; + } + } + } + + if (px == 0) { + nvkm_debug(subdev, "WAIT VBLANK !NO ACTIVE HEAD\n"); + return; + } + + nvkm_debug(subdev, "WAIT VBLANK HEAD%d\n", head_sync); + nvkm_hwsq_wait(hwsq, head_sync ? 0x3 : 0x1, 0x0); + nvkm_hwsq_wait(hwsq, head_sync ? 0x3 : 0x1, 0x1); +} + +void +nvkm_hwsq_nsec(struct nvkm_hwsq *hwsq, u32 nsec) +{ + u8 shift = 0, usec = nsec / 1000; + while (usec & ~3) { + usec >>= 2; + shift++; + } + + nvkm_debug(hwsq->subdev, " DELAY = %d ns\n", nsec); + hwsq_cmd(hwsq, 1, (u8[]){ 0x00 | (shift << 2) | usec }); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h new file mode 100644 index 000000000..217a0a4a3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_BUS_HWSQ_H__ +#define __NVKM_BUS_HWSQ_H__ +#include + +struct hwsq { + struct nvkm_subdev *subdev; + struct nvkm_hwsq *hwsq; + int sequence; +}; + +struct hwsq_reg { + int sequence; + bool force; + u32 addr; + u32 stride; /* in bytes */ + u32 mask; + u32 data; +}; + +static inline struct hwsq_reg +hwsq_stride(u32 addr, u32 stride, u32 mask) +{ + return (struct hwsq_reg) { + .sequence = 0, + .force = 0, + .addr = addr, + .stride = stride, + .mask = mask, + .data = 0xdeadbeef, + }; +} + +static inline struct hwsq_reg +hwsq_reg2(u32 addr1, u32 addr2) +{ + return (struct hwsq_reg) { + .sequence = 0, + .force = 0, + .addr = addr1, + .stride = addr2 - addr1, + .mask = 0x3, + .data = 0xdeadbeef, + }; +} + +static inline struct hwsq_reg +hwsq_reg(u32 addr) +{ + return (struct hwsq_reg) { + .sequence = 0, + .force = 0, + .addr = addr, + .stride = 0, + .mask = 0x1, + .data = 0xdeadbeef, + }; +} + +static inline int +hwsq_init(struct hwsq *ram, struct nvkm_subdev *subdev) +{ + int ret; + + ret = nvkm_hwsq_init(subdev, &ram->hwsq); + if (ret) + return ret; + + ram->sequence++; + ram->subdev = subdev; + return 0; +} + +static inline int +hwsq_exec(struct hwsq *ram, bool exec) +{ + int ret = 0; + if (ram->subdev) { + ret = nvkm_hwsq_fini(&ram->hwsq, exec); + ram->subdev = NULL; + } + return ret; +} + +static inline u32 +hwsq_rd32(struct hwsq *ram, struct hwsq_reg *reg) +{ + struct nvkm_device *device = ram->subdev->device; + if (reg->sequence != ram->sequence) + reg->data = nvkm_rd32(device, reg->addr); + return reg->data; +} + +static inline void +hwsq_wr32(struct hwsq *ram, struct hwsq_reg *reg, u32 data) +{ + u32 mask, off = 0; + + reg->sequence = ram->sequence; + reg->data = data; + + for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) { + if (mask & 1) + nvkm_hwsq_wr32(ram->hwsq, reg->addr+off, reg->data); + + off += reg->stride; + } +} + +static inline void +hwsq_nuke(struct hwsq *ram, struct hwsq_reg *reg) +{ + reg->force = true; +} + +static inline u32 +hwsq_mask(struct hwsq *ram, struct hwsq_reg *reg, u32 mask, u32 data) +{ + u32 temp = hwsq_rd32(ram, reg); + if (temp != ((temp & ~mask) | data) || reg->force) + hwsq_wr32(ram, reg, (temp & ~mask) | data); + return temp; +} + +static inline void +hwsq_setf(struct hwsq *ram, u8 flag, int data) +{ + nvkm_hwsq_setf(ram->hwsq, flag, data); +} + +static inline void +hwsq_wait(struct hwsq *ram, u8 flag, u8 data) +{ + nvkm_hwsq_wait(ram->hwsq, flag, data); +} + +static inline void +hwsq_wait_vblank(struct hwsq *ram) +{ + nvkm_hwsq_wait_vblank(ram->hwsq); +} + +static inline void +hwsq_nsec(struct hwsq *ram, u32 nsec) +{ + nvkm_hwsq_nsec(ram->hwsq, nsec); +} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c new file mode 100644 index 000000000..cfed17c06 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c @@ -0,0 +1,75 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + * Ben Skeggs + */ +#include "priv.h" + +#include + +static void +nv04_bus_intr(struct nvkm_bus *bus) +{ + struct nvkm_subdev *subdev = &bus->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x001100) & nvkm_rd32(device, 0x001140); + + if (stat & 0x00000001) { + nvkm_error(subdev, "BUS ERROR\n"); + stat &= ~0x00000001; + nvkm_wr32(device, 0x001100, 0x00000001); + } + + if (stat & 0x00000110) { + struct nvkm_gpio *gpio = device->gpio; + if (gpio) + nvkm_subdev_intr(&gpio->subdev); + stat &= ~0x00000110; + nvkm_wr32(device, 0x001100, 0x00000110); + } + + if (stat) { + nvkm_error(subdev, "intr %08x\n", stat); + nvkm_mask(device, 0x001140, stat, 0x00000000); + } +} + +static void +nv04_bus_init(struct nvkm_bus *bus) +{ + struct nvkm_device *device = bus->subdev.device; + nvkm_wr32(device, 0x001100, 0xffffffff); + nvkm_wr32(device, 0x001140, 0x00000111); +} + +static const struct nvkm_bus_func +nv04_bus = { + .init = nv04_bus_init, + .intr = nv04_bus_intr, +}; + +int +nv04_bus_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bus **pbus) +{ + return nvkm_bus_new_(&nv04_bus, device, type, inst, pbus); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c new file mode 100644 index 000000000..c75e463f3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + * Ben Skeggs + */ +#include "priv.h" + +#include +#include + +static void +nv31_bus_intr(struct nvkm_bus *bus) +{ + struct nvkm_subdev *subdev = &bus->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x001100) & nvkm_rd32(device, 0x001140); + u32 gpio = nvkm_rd32(device, 0x001104) & nvkm_rd32(device, 0x001144); + + if (gpio) { + struct nvkm_gpio *gpio = device->gpio; + if (gpio) + nvkm_subdev_intr(&gpio->subdev); + } + + if (stat & 0x00000008) { /* NV41- */ + u32 addr = nvkm_rd32(device, 0x009084); + u32 data = nvkm_rd32(device, 0x009088); + + nvkm_error_ratelimited(subdev, "MMIO %s of %08x FAULT at %06x\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc)); + + stat &= ~0x00000008; + nvkm_wr32(device, 0x001100, 0x00000008); + } + + if (stat & 0x00070000) { + struct nvkm_therm *therm = device->therm; + if (therm) + nvkm_subdev_intr(&therm->subdev); + stat &= ~0x00070000; + nvkm_wr32(device, 0x001100, 0x00070000); + } + + if (stat) { + nvkm_error(subdev, "intr %08x\n", stat); + nvkm_mask(device, 0x001140, stat, 0x00000000); + } +} + +static void +nv31_bus_init(struct nvkm_bus *bus) +{ + struct nvkm_device *device = bus->subdev.device; + nvkm_wr32(device, 0x001100, 0xffffffff); + nvkm_wr32(device, 0x001140, 0x00070008); +} + +static const struct nvkm_bus_func +nv31_bus = { + .init = nv31_bus_init, + .intr = nv31_bus_intr, +}; + +int +nv31_bus_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bus **pbus) +{ + return nvkm_bus_new_(&nv31_bus, device, type, inst, pbus); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c new file mode 100644 index 000000000..2055d0b10 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c @@ -0,0 +1,106 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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: Martin Peres + * Ben Skeggs + */ +#include "priv.h" + +#include +#include + +static int +nv50_bus_hwsq_exec(struct nvkm_bus *bus, u32 *data, u32 size) +{ + struct nvkm_device *device = bus->subdev.device; + int i; + + nvkm_mask(device, 0x001098, 0x00000008, 0x00000000); + nvkm_wr32(device, 0x001304, 0x00000000); + for (i = 0; i < size; i++) + nvkm_wr32(device, 0x001400 + (i * 4), data[i]); + nvkm_mask(device, 0x001098, 0x00000018, 0x00000018); + nvkm_wr32(device, 0x00130c, 0x00000003); + + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x001308) & 0x00000100)) + break; + ) < 0) + return -ETIMEDOUT; + + return 0; +} + +void +nv50_bus_intr(struct nvkm_bus *bus) +{ + struct nvkm_subdev *subdev = &bus->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x001100) & nvkm_rd32(device, 0x001140); + + if (stat & 0x00000008) { + u32 addr = nvkm_rd32(device, 0x009084); + u32 data = nvkm_rd32(device, 0x009088); + + nvkm_error_ratelimited(subdev, "MMIO %s of %08x FAULT at %06x\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc)); + + stat &= ~0x00000008; + nvkm_wr32(device, 0x001100, 0x00000008); + } + + if (stat & 0x00010000) { + struct nvkm_therm *therm = device->therm; + if (therm) + nvkm_subdev_intr(&therm->subdev); + stat &= ~0x00010000; + nvkm_wr32(device, 0x001100, 0x00010000); + } + + if (stat) { + nvkm_error(subdev, "intr %08x\n", stat); + nvkm_mask(device, 0x001140, stat, 0); + } +} + +void +nv50_bus_init(struct nvkm_bus *bus) +{ + struct nvkm_device *device = bus->subdev.device; + nvkm_wr32(device, 0x001100, 0xffffffff); + nvkm_wr32(device, 0x001140, 0x00010008); +} + +static const struct nvkm_bus_func +nv50_bus = { + .init = nv50_bus_init, + .intr = nv50_bus_intr, + .hwsq_exec = nv50_bus_hwsq_exec, + .hwsq_size = 64, +}; + +int +nv50_bus_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_bus **pbus) +{ + return nvkm_bus_new_(&nv50_bus, device, type, inst, pbus); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/priv.h new file mode 100644 index 000000000..2e9345b17 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/priv.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_BUS_PRIV_H__ +#define __NVKM_BUS_PRIV_H__ +#define nvkm_bus(p) container_of((p), struct nvkm_bus, subdev) +#include + +struct nvkm_bus_func { + void (*init)(struct nvkm_bus *); + void (*intr)(struct nvkm_bus *); + int (*hwsq_exec)(struct nvkm_bus *, u32 *, u32); + u32 hwsq_size; +}; + +int nvkm_bus_new_(const struct nvkm_bus_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_bus **); + +void nv50_bus_init(struct nvkm_bus *); +void nv50_bus_intr(struct nvkm_bus *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild new file mode 100644 index 000000000..dcecd499d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/clk/base.o +nvkm-y += nvkm/subdev/clk/nv04.o +nvkm-y += nvkm/subdev/clk/nv40.o +nvkm-y += nvkm/subdev/clk/nv50.o +nvkm-y += nvkm/subdev/clk/g84.o +nvkm-y += nvkm/subdev/clk/gt215.o +nvkm-y += nvkm/subdev/clk/mcp77.o +nvkm-y += nvkm/subdev/clk/gf100.o +nvkm-y += nvkm/subdev/clk/gk104.o +nvkm-y += nvkm/subdev/clk/gk20a.o +nvkm-y += nvkm/subdev/clk/gm20b.o + +nvkm-y += nvkm/subdev/clk/pllnv04.o +nvkm-y += nvkm/subdev/clk/pllgt215.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c new file mode 100644 index 000000000..da07a2fbe --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c @@ -0,0 +1,716 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/****************************************************************************** + * misc + *****************************************************************************/ +static u32 +nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, + u8 pstate, u8 domain, u32 input) +{ + struct nvkm_bios *bios = clk->subdev.device->bios; + struct nvbios_boostE boostE; + u8 ver, hdr, cnt, len; + u32 data; + + data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE); + if (data) { + struct nvbios_boostS boostS; + u8 idx = 0, sver, shdr; + u32 subd; + + input = max(boostE.min, input); + input = min(boostE.max, input); + do { + sver = ver; + shdr = hdr; + subd = nvbios_boostSp(bios, idx++, data, &sver, &shdr, + cnt, len, &boostS); + if (subd && boostS.domain == domain) { + if (adjust) + input = input * boostS.percent / 100; + input = max(boostS.min, input); + input = min(boostS.max, input); + break; + } + } while (subd); + } + + return input; +} + +/****************************************************************************** + * C-States + *****************************************************************************/ +static bool +nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, + u32 max_volt, int temp) +{ + const struct nvkm_domain *domain = clk->domains; + struct nvkm_volt *volt = clk->subdev.device->volt; + int voltage; + + while (domain && domain->name != nv_clk_src_max) { + if (domain->flags & NVKM_CLK_DOM_FLAG_VPSTATE) { + u32 freq = cstate->domain[domain->name]; + switch (clk->boost_mode) { + case NVKM_CLK_BOOST_NONE: + if (clk->base_khz && freq > clk->base_khz) + return false; + fallthrough; + case NVKM_CLK_BOOST_BIOS: + if (clk->boost_khz && freq > clk->boost_khz) + return false; + } + } + domain++; + } + + if (!volt) + return true; + + voltage = nvkm_volt_map(volt, cstate->voltage, temp); + if (voltage < 0) + return false; + return voltage <= min(max_volt, volt->max_uv); +} + +static struct nvkm_cstate * +nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, + struct nvkm_cstate *cstate) +{ + struct nvkm_device *device = clk->subdev.device; + struct nvkm_volt *volt = device->volt; + int max_volt; + + if (!pstate || !cstate) + return NULL; + + if (!volt) + return cstate; + + max_volt = volt->max_uv; + if (volt->max0_id != 0xff) + max_volt = min(max_volt, + nvkm_volt_map(volt, volt->max0_id, clk->temp)); + if (volt->max1_id != 0xff) + max_volt = min(max_volt, + nvkm_volt_map(volt, volt->max1_id, clk->temp)); + if (volt->max2_id != 0xff) + max_volt = min(max_volt, + nvkm_volt_map(volt, volt->max2_id, clk->temp)); + + list_for_each_entry_from_reverse(cstate, &pstate->list, head) { + if (nvkm_cstate_valid(clk, cstate, max_volt, clk->temp)) + return cstate; + } + + return NULL; +} + +static struct nvkm_cstate * +nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) +{ + struct nvkm_cstate *cstate; + if (cstatei == NVKM_CLK_CSTATE_HIGHEST) + return list_last_entry(&pstate->list, typeof(*cstate), head); + else { + list_for_each_entry(cstate, &pstate->list, head) { + if (cstate->id == cstatei) + return cstate; + } + } + return NULL; +} + +static int +nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) +{ + struct nvkm_subdev *subdev = &clk->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_therm *therm = device->therm; + struct nvkm_volt *volt = device->volt; + struct nvkm_cstate *cstate; + int ret; + + if (!list_empty(&pstate->list)) { + cstate = nvkm_cstate_get(clk, pstate, cstatei); + cstate = nvkm_cstate_find_best(clk, pstate, cstate); + if (!cstate) + return -EINVAL; + } else { + cstate = &pstate->base; + } + + if (therm) { + ret = nvkm_therm_cstate(therm, pstate->fanspeed, +1); + if (ret && ret != -ENODEV) { + nvkm_error(subdev, "failed to raise fan speed: %d\n", ret); + return ret; + } + } + + if (volt) { + ret = nvkm_volt_set_id(volt, cstate->voltage, + pstate->base.voltage, clk->temp, +1); + if (ret && ret != -ENODEV) { + nvkm_error(subdev, "failed to raise voltage: %d\n", ret); + return ret; + } + } + + ret = clk->func->calc(clk, cstate); + if (ret == 0) { + ret = clk->func->prog(clk); + clk->func->tidy(clk); + } + + if (volt) { + ret = nvkm_volt_set_id(volt, cstate->voltage, + pstate->base.voltage, clk->temp, -1); + if (ret && ret != -ENODEV) + nvkm_error(subdev, "failed to lower voltage: %d\n", ret); + } + + if (therm) { + ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1); + if (ret && ret != -ENODEV) + nvkm_error(subdev, "failed to lower fan speed: %d\n", ret); + } + + return ret; +} + +static void +nvkm_cstate_del(struct nvkm_cstate *cstate) +{ + list_del(&cstate->head); + kfree(cstate); +} + +static int +nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) +{ + struct nvkm_bios *bios = clk->subdev.device->bios; + struct nvkm_volt *volt = clk->subdev.device->volt; + const struct nvkm_domain *domain = clk->domains; + struct nvkm_cstate *cstate = NULL; + struct nvbios_cstepX cstepX; + u8 ver, hdr; + u32 data; + + data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX); + if (!data) + return -ENOENT; + + if (volt && nvkm_volt_map_min(volt, cstepX.voltage) > volt->max_uv) + return -EINVAL; + + cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); + if (!cstate) + return -ENOMEM; + + *cstate = pstate->base; + cstate->voltage = cstepX.voltage; + cstate->id = idx; + + while (domain && domain->name != nv_clk_src_max) { + if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { + u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate, + domain->bios, cstepX.freq); + cstate->domain[domain->name] = freq; + } + domain++; + } + + list_add(&cstate->head, &pstate->list); + return 0; +} + +/****************************************************************************** + * P-States + *****************************************************************************/ +static int +nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) +{ + struct nvkm_subdev *subdev = &clk->subdev; + struct nvkm_fb *fb = subdev->device->fb; + struct nvkm_pci *pci = subdev->device->pci; + struct nvkm_pstate *pstate; + int ret, idx = 0; + + list_for_each_entry(pstate, &clk->states, head) { + if (idx++ == pstatei) + break; + } + + nvkm_debug(subdev, "setting performance state %d\n", pstatei); + clk->pstate = pstatei; + + nvkm_pcie_set_link(pci, pstate->pcie_speed, pstate->pcie_width); + + if (fb && fb->ram && fb->ram->func->calc) { + struct nvkm_ram *ram = fb->ram; + int khz = pstate->base.domain[nv_clk_src_mem]; + do { + ret = ram->func->calc(ram, khz); + if (ret == 0) + ret = ram->func->prog(ram); + } while (ret > 0); + ram->func->tidy(ram); + } + + return nvkm_cstate_prog(clk, pstate, NVKM_CLK_CSTATE_HIGHEST); +} + +static void +nvkm_pstate_work(struct work_struct *work) +{ + struct nvkm_clk *clk = container_of(work, typeof(*clk), work); + struct nvkm_subdev *subdev = &clk->subdev; + int pstate; + + if (!atomic_xchg(&clk->waiting, 0)) + return; + clk->pwrsrc = power_supply_is_system_supplied(); + + nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d°C D %d\n", + clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, + clk->astate, clk->temp, clk->dstate); + + pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; + if (clk->state_nr && pstate != -1) { + pstate = (pstate < 0) ? clk->astate : pstate; + pstate = min(pstate, clk->state_nr - 1); + pstate = max(pstate, clk->dstate); + } else { + pstate = clk->pstate = -1; + } + + nvkm_trace(subdev, "-> %d\n", pstate); + if (pstate != clk->pstate) { + int ret = nvkm_pstate_prog(clk, pstate); + if (ret) { + nvkm_error(subdev, "error setting pstate %d: %d\n", + pstate, ret); + } + } + + wake_up_all(&clk->wait); +} + +static int +nvkm_pstate_calc(struct nvkm_clk *clk, bool wait) +{ + atomic_set(&clk->waiting, 1); + schedule_work(&clk->work); + if (wait) + wait_event(clk->wait, !atomic_read(&clk->waiting)); + return 0; +} + +static void +nvkm_pstate_info(struct nvkm_clk *clk, struct nvkm_pstate *pstate) +{ + const struct nvkm_domain *clock = clk->domains - 1; + struct nvkm_cstate *cstate; + struct nvkm_subdev *subdev = &clk->subdev; + char info[3][32] = { "", "", "" }; + char name[4] = "--"; + int i = -1; + + if (pstate->pstate != 0xff) + snprintf(name, sizeof(name), "%02x", pstate->pstate); + + while ((++clock)->name != nv_clk_src_max) { + u32 lo = pstate->base.domain[clock->name]; + u32 hi = lo; + if (hi == 0) + continue; + + nvkm_debug(subdev, "%02x: %10d KHz\n", clock->name, lo); + list_for_each_entry(cstate, &pstate->list, head) { + u32 freq = cstate->domain[clock->name]; + lo = min(lo, freq); + hi = max(hi, freq); + nvkm_debug(subdev, "%10d KHz\n", freq); + } + + if (clock->mname && ++i < ARRAY_SIZE(info)) { + lo /= clock->mdiv; + hi /= clock->mdiv; + if (lo == hi) { + snprintf(info[i], sizeof(info[i]), "%s %d MHz", + clock->mname, lo); + } else { + snprintf(info[i], sizeof(info[i]), + "%s %d-%d MHz", clock->mname, lo, hi); + } + } + } + + nvkm_debug(subdev, "%s: %s %s %s\n", name, info[0], info[1], info[2]); +} + +static void +nvkm_pstate_del(struct nvkm_pstate *pstate) +{ + struct nvkm_cstate *cstate, *temp; + + list_for_each_entry_safe(cstate, temp, &pstate->list, head) { + nvkm_cstate_del(cstate); + } + + list_del(&pstate->head); + kfree(pstate); +} + +static int +nvkm_pstate_new(struct nvkm_clk *clk, int idx) +{ + struct nvkm_bios *bios = clk->subdev.device->bios; + const struct nvkm_domain *domain = clk->domains - 1; + struct nvkm_pstate *pstate; + struct nvkm_cstate *cstate; + struct nvbios_cstepE cstepE; + struct nvbios_perfE perfE; + u8 ver, hdr, cnt, len; + u32 data; + + data = nvbios_perfEp(bios, idx, &ver, &hdr, &cnt, &len, &perfE); + if (!data) + return -EINVAL; + if (perfE.pstate == 0xff) + return 0; + + pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); + cstate = &pstate->base; + if (!pstate) + return -ENOMEM; + + INIT_LIST_HEAD(&pstate->list); + + pstate->pstate = perfE.pstate; + pstate->fanspeed = perfE.fanspeed; + pstate->pcie_speed = perfE.pcie_speed; + pstate->pcie_width = perfE.pcie_width; + cstate->voltage = perfE.voltage; + cstate->domain[nv_clk_src_core] = perfE.core; + cstate->domain[nv_clk_src_shader] = perfE.shader; + cstate->domain[nv_clk_src_mem] = perfE.memory; + cstate->domain[nv_clk_src_vdec] = perfE.vdec; + cstate->domain[nv_clk_src_dom6] = perfE.disp; + + while (ver >= 0x40 && (++domain)->name != nv_clk_src_max) { + struct nvbios_perfS perfS; + u8 sver = ver, shdr = hdr; + u32 perfSe = nvbios_perfSp(bios, data, domain->bios, + &sver, &shdr, cnt, len, &perfS); + if (perfSe == 0 || sver != 0x40) + continue; + + if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { + perfS.v40.freq = nvkm_clk_adjust(clk, false, + pstate->pstate, + domain->bios, + perfS.v40.freq); + } + + cstate->domain[domain->name] = perfS.v40.freq; + } + + data = nvbios_cstepEm(bios, pstate->pstate, &ver, &hdr, &cstepE); + if (data) { + int idx = cstepE.index; + do { + nvkm_cstate_new(clk, idx, pstate); + } while(idx--); + } + + nvkm_pstate_info(clk, pstate); + list_add_tail(&pstate->head, &clk->states); + clk->state_nr++; + return 0; +} + +/****************************************************************************** + * Adjustment triggers + *****************************************************************************/ +static int +nvkm_clk_ustate_update(struct nvkm_clk *clk, int req) +{ + struct nvkm_pstate *pstate; + int i = 0; + + if (!clk->allow_reclock) + return -ENOSYS; + + if (req != -1 && req != -2) { + list_for_each_entry(pstate, &clk->states, head) { + if (pstate->pstate == req) + break; + i++; + } + + if (pstate->pstate != req) + return -EINVAL; + req = i; + } + + return req + 2; +} + +static int +nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode, int arglen) +{ + int ret = 1; + + if (clk->allow_reclock && !strncasecmpz(mode, "auto", arglen)) + return -2; + + if (strncasecmpz(mode, "disabled", arglen)) { + char save = mode[arglen]; + long v; + + ((char *)mode)[arglen] = '\0'; + if (!kstrtol(mode, 0, &v)) { + ret = nvkm_clk_ustate_update(clk, v); + if (ret < 0) + ret = 1; + } + ((char *)mode)[arglen] = save; + } + + return ret - 2; +} + +int +nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr) +{ + int ret = nvkm_clk_ustate_update(clk, req); + if (ret >= 0) { + if (ret -= 2, pwr) clk->ustate_ac = ret; + else clk->ustate_dc = ret; + return nvkm_pstate_calc(clk, true); + } + return ret; +} + +int +nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait) +{ + if (!rel) clk->astate = req; + if ( rel) clk->astate += rel; + clk->astate = min(clk->astate, clk->state_nr - 1); + clk->astate = max(clk->astate, 0); + return nvkm_pstate_calc(clk, wait); +} + +int +nvkm_clk_tstate(struct nvkm_clk *clk, u8 temp) +{ + if (clk->temp == temp) + return 0; + clk->temp = temp; + return nvkm_pstate_calc(clk, false); +} + +int +nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel) +{ + if (!rel) clk->dstate = req; + if ( rel) clk->dstate += rel; + clk->dstate = min(clk->dstate, clk->state_nr - 1); + clk->dstate = max(clk->dstate, 0); + return nvkm_pstate_calc(clk, true); +} + +int +nvkm_clk_pwrsrc(struct nvkm_device *device) +{ + if (device->clk) + return nvkm_pstate_calc(device->clk, false); + return 0; +} + +/****************************************************************************** + * subdev base class implementation + *****************************************************************************/ + +int +nvkm_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) +{ + return clk->func->read(clk, src); +} + +static int +nvkm_clk_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_clk *clk = nvkm_clk(subdev); + flush_work(&clk->work); + if (clk->func->fini) + clk->func->fini(clk); + return 0; +} + +static int +nvkm_clk_init(struct nvkm_subdev *subdev) +{ + struct nvkm_clk *clk = nvkm_clk(subdev); + const struct nvkm_domain *clock = clk->domains; + int ret; + + memset(&clk->bstate, 0x00, sizeof(clk->bstate)); + INIT_LIST_HEAD(&clk->bstate.list); + clk->bstate.pstate = 0xff; + + while (clock->name != nv_clk_src_max) { + ret = nvkm_clk_read(clk, clock->name); + if (ret < 0) { + nvkm_error(subdev, "%02x freq unknown\n", clock->name); + return ret; + } + clk->bstate.base.domain[clock->name] = ret; + clock++; + } + + nvkm_pstate_info(clk, &clk->bstate); + + if (clk->func->init) + return clk->func->init(clk); + + clk->astate = clk->state_nr - 1; + clk->dstate = 0; + clk->pstate = -1; + clk->temp = 90; /* reasonable default value */ + nvkm_pstate_calc(clk, true); + return 0; +} + +static void * +nvkm_clk_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_clk *clk = nvkm_clk(subdev); + struct nvkm_pstate *pstate, *temp; + + /* Early return if the pstates have been provided statically */ + if (clk->func->pstates) + return clk; + + list_for_each_entry_safe(pstate, temp, &clk->states, head) { + nvkm_pstate_del(pstate); + } + + return clk; +} + +static const struct nvkm_subdev_func +nvkm_clk = { + .dtor = nvkm_clk_dtor, + .init = nvkm_clk_init, + .fini = nvkm_clk_fini, +}; + +int +nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool allow_reclock, struct nvkm_clk *clk) +{ + struct nvkm_subdev *subdev = &clk->subdev; + struct nvkm_bios *bios = device->bios; + int ret, idx, arglen; + const char *mode; + struct nvbios_vpstate_header h; + + nvkm_subdev_ctor(&nvkm_clk, device, type, inst, subdev); + + if (bios && !nvbios_vpstate_parse(bios, &h)) { + struct nvbios_vpstate_entry base, boost; + if (!nvbios_vpstate_entry(bios, &h, h.boost_id, &boost)) + clk->boost_khz = boost.clock_mhz * 1000; + if (!nvbios_vpstate_entry(bios, &h, h.base_id, &base)) + clk->base_khz = base.clock_mhz * 1000; + } + + clk->func = func; + INIT_LIST_HEAD(&clk->states); + clk->domains = func->domains; + clk->ustate_ac = -1; + clk->ustate_dc = -1; + clk->allow_reclock = allow_reclock; + + INIT_WORK(&clk->work, nvkm_pstate_work); + init_waitqueue_head(&clk->wait); + atomic_set(&clk->waiting, 0); + + /* If no pstates are provided, try and fetch them from the BIOS */ + if (!func->pstates) { + idx = 0; + do { + ret = nvkm_pstate_new(clk, idx++); + } while (ret == 0); + } else { + for (idx = 0; idx < func->nr_pstates; idx++) + list_add_tail(&func->pstates[idx].head, &clk->states); + clk->state_nr = func->nr_pstates; + } + + mode = nvkm_stropt(device->cfgopt, "NvClkMode", &arglen); + if (mode) { + clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); + clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); + } + + mode = nvkm_stropt(device->cfgopt, "NvClkModeAC", &arglen); + if (mode) + clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); + + mode = nvkm_stropt(device->cfgopt, "NvClkModeDC", &arglen); + if (mode) + clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); + + clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost", + NVKM_CLK_BOOST_NONE); + return 0; +} + +int +nvkm_clk_new_(const struct nvkm_clk_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool allow_reclock, struct nvkm_clk **pclk) +{ + if (!(*pclk = kzalloc(sizeof(**pclk), GFP_KERNEL))) + return -ENOMEM; + return nvkm_clk_ctor(func, device, type, inst, allow_reclock, *pclk); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c new file mode 100644 index 000000000..07157cf53 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c @@ -0,0 +1,48 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +static const struct nvkm_clk_func +g84_clk = { + .read = nv50_clk_read, + .calc = nv50_clk_calc, + .prog = nv50_clk_prog, + .tidy = nv50_clk_tidy, + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_href , 0xff }, + { nv_clk_src_core , 0xff, 0, "core", 1000 }, + { nv_clk_src_shader , 0xff, 0, "shader", 1000 }, + { nv_clk_src_mem , 0xff, 0, "memory", 1000 }, + { nv_clk_src_vdec , 0xff }, + { nv_clk_src_max } + } +}; + +int +g84_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + return nv50_clk_new_(&g84_clk, device, type, inst, (device->chipset >= 0x94), pclk); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c new file mode 100644 index 000000000..6eea11aef --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c @@ -0,0 +1,481 @@ +/* + * 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 + */ +#define gf100_clk(p) container_of((p), struct gf100_clk, base) +#include "priv.h" +#include "pll.h" + +#include +#include +#include + +struct gf100_clk_info { + u32 freq; + u32 ssel; + u32 mdiv; + u32 dsrc; + u32 ddiv; + u32 coef; +}; + +struct gf100_clk { + struct nvkm_clk base; + struct gf100_clk_info eng[16]; +}; + +static u32 read_div(struct gf100_clk *, int, u32, u32); + +static u32 +read_vco(struct gf100_clk *clk, u32 dsrc) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ssrc = nvkm_rd32(device, dsrc); + if (!(ssrc & 0x00000100)) + return nvkm_clk_read(&clk->base, nv_clk_src_sppll0); + return nvkm_clk_read(&clk->base, nv_clk_src_sppll1); +} + +static u32 +read_pll(struct gf100_clk *clk, u32 pll) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ctrl = nvkm_rd32(device, pll + 0x00); + u32 coef = nvkm_rd32(device, pll + 0x04); + u32 P = (coef & 0x003f0000) >> 16; + u32 N = (coef & 0x0000ff00) >> 8; + u32 M = (coef & 0x000000ff) >> 0; + u32 sclk; + + if (!(ctrl & 0x00000001)) + return 0; + + switch (pll) { + case 0x00e800: + case 0x00e820: + sclk = device->crystal; + P = 1; + break; + case 0x132000: + sclk = nvkm_clk_read(&clk->base, nv_clk_src_mpllsrc); + break; + case 0x132020: + sclk = nvkm_clk_read(&clk->base, nv_clk_src_mpllsrcref); + break; + case 0x137000: + case 0x137020: + case 0x137040: + case 0x1370e0: + sclk = read_div(clk, (pll & 0xff) / 0x20, 0x137120, 0x137140); + break; + default: + return 0; + } + + return sclk * N / M / P; +} + +static u32 +read_div(struct gf100_clk *clk, int doff, u32 dsrc, u32 dctl) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ssrc = nvkm_rd32(device, dsrc + (doff * 4)); + u32 sclk, sctl, sdiv = 2; + + switch (ssrc & 0x00000003) { + case 0: + if ((ssrc & 0x00030000) != 0x00030000) + return device->crystal; + return 108000; + case 2: + return 100000; + case 3: + sclk = read_vco(clk, dsrc + (doff * 4)); + + /* Memclk has doff of 0 despite its alt. location */ + if (doff <= 2) { + sctl = nvkm_rd32(device, dctl + (doff * 4)); + + if (sctl & 0x80000000) { + if (ssrc & 0x100) + sctl >>= 8; + + sdiv = (sctl & 0x3f) + 2; + } + } + + return (sclk * 2) / sdiv; + default: + return 0; + } +} + +static u32 +read_clk(struct gf100_clk *clk, int idx) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 sctl = nvkm_rd32(device, 0x137250 + (idx * 4)); + u32 ssel = nvkm_rd32(device, 0x137100); + u32 sclk, sdiv; + + if (ssel & (1 << idx)) { + if (idx < 7) + sclk = read_pll(clk, 0x137000 + (idx * 0x20)); + else + sclk = read_pll(clk, 0x1370e0); + sdiv = ((sctl & 0x00003f00) >> 8) + 2; + } else { + sclk = read_div(clk, idx, 0x137160, 0x1371d0); + sdiv = ((sctl & 0x0000003f) >> 0) + 2; + } + + if (sctl & 0x80000000) + return (sclk * 2) / sdiv; + + return sclk; +} + +static int +gf100_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct gf100_clk *clk = gf100_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + + switch (src) { + case nv_clk_src_crystal: + return device->crystal; + case nv_clk_src_href: + return 100000; + case nv_clk_src_sppll0: + return read_pll(clk, 0x00e800); + case nv_clk_src_sppll1: + return read_pll(clk, 0x00e820); + + case nv_clk_src_mpllsrcref: + return read_div(clk, 0, 0x137320, 0x137330); + case nv_clk_src_mpllsrc: + return read_pll(clk, 0x132020); + case nv_clk_src_mpll: + return read_pll(clk, 0x132000); + case nv_clk_src_mdiv: + return read_div(clk, 0, 0x137300, 0x137310); + case nv_clk_src_mem: + if (nvkm_rd32(device, 0x1373f0) & 0x00000002) + return nvkm_clk_read(&clk->base, nv_clk_src_mpll); + return nvkm_clk_read(&clk->base, nv_clk_src_mdiv); + + case nv_clk_src_gpc: + return read_clk(clk, 0x00); + case nv_clk_src_rop: + return read_clk(clk, 0x01); + case nv_clk_src_hubk07: + return read_clk(clk, 0x02); + case nv_clk_src_hubk06: + return read_clk(clk, 0x07); + case nv_clk_src_hubk01: + return read_clk(clk, 0x08); + case nv_clk_src_copy: + return read_clk(clk, 0x09); + case nv_clk_src_pmu: + return read_clk(clk, 0x0c); + case nv_clk_src_vdec: + return read_clk(clk, 0x0e); + default: + nvkm_error(subdev, "invalid clock source %d\n", src); + return -EINVAL; + } +} + +static u32 +calc_div(struct gf100_clk *clk, int idx, u32 ref, u32 freq, u32 *ddiv) +{ + u32 div = min((ref * 2) / freq, (u32)65); + if (div < 2) + div = 2; + + *ddiv = div - 2; + return (ref * 2) / div; +} + +static u32 +calc_src(struct gf100_clk *clk, int idx, u32 freq, u32 *dsrc, u32 *ddiv) +{ + u32 sclk; + + /* use one of the fixed frequencies if possible */ + *ddiv = 0x00000000; + switch (freq) { + case 27000: + case 108000: + *dsrc = 0x00000000; + if (freq == 108000) + *dsrc |= 0x00030000; + return freq; + case 100000: + *dsrc = 0x00000002; + return freq; + default: + *dsrc = 0x00000003; + break; + } + + /* otherwise, calculate the closest divider */ + sclk = read_vco(clk, 0x137160 + (idx * 4)); + if (idx < 7) + sclk = calc_div(clk, idx, sclk, freq, ddiv); + return sclk; +} + +static u32 +calc_pll(struct gf100_clk *clk, int idx, u32 freq, u32 *coef) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_pll limits; + int N, M, P, ret; + + ret = nvbios_pll_parse(bios, 0x137000 + (idx * 0x20), &limits); + if (ret) + return 0; + + limits.refclk = read_div(clk, idx, 0x137120, 0x137140); + if (!limits.refclk) + return 0; + + ret = gt215_pll_calc(subdev, &limits, freq, &N, NULL, &M, &P); + if (ret <= 0) + return 0; + + *coef = (P << 16) | (N << 8) | M; + return ret; +} + +static int +calc_clk(struct gf100_clk *clk, struct nvkm_cstate *cstate, int idx, int dom) +{ + struct gf100_clk_info *info = &clk->eng[idx]; + u32 freq = cstate->domain[dom]; + u32 src0, div0, div1D, div1P = 0; + u32 clk0, clk1 = 0; + + /* invalid clock domain */ + if (!freq) + return 0; + + /* first possible path, using only dividers */ + clk0 = calc_src(clk, idx, freq, &src0, &div0); + clk0 = calc_div(clk, idx, clk0, freq, &div1D); + + /* see if we can get any closer using PLLs */ + if (clk0 != freq && (0x00004387 & (1 << idx))) { + if (idx <= 7) + clk1 = calc_pll(clk, idx, freq, &info->coef); + else + clk1 = cstate->domain[nv_clk_src_hubk06]; + clk1 = calc_div(clk, idx, clk1, freq, &div1P); + } + + /* select the method which gets closest to target freq */ + if (abs((int)freq - clk0) <= abs((int)freq - clk1)) { + info->dsrc = src0; + if (div0) { + info->ddiv |= 0x80000000; + info->ddiv |= div0 << 8; + info->ddiv |= div0; + } + if (div1D) { + info->mdiv |= 0x80000000; + info->mdiv |= div1D; + } + info->ssel = info->coef = 0; + info->freq = clk0; + } else { + if (div1P) { + info->mdiv |= 0x80000000; + info->mdiv |= div1P << 8; + } + info->ssel = (1 << idx); + info->freq = clk1; + } + + return 0; +} + +static int +gf100_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct gf100_clk *clk = gf100_clk(base); + int ret; + + if ((ret = calc_clk(clk, cstate, 0x00, nv_clk_src_gpc)) || + (ret = calc_clk(clk, cstate, 0x01, nv_clk_src_rop)) || + (ret = calc_clk(clk, cstate, 0x02, nv_clk_src_hubk07)) || + (ret = calc_clk(clk, cstate, 0x07, nv_clk_src_hubk06)) || + (ret = calc_clk(clk, cstate, 0x08, nv_clk_src_hubk01)) || + (ret = calc_clk(clk, cstate, 0x09, nv_clk_src_copy)) || + (ret = calc_clk(clk, cstate, 0x0c, nv_clk_src_pmu)) || + (ret = calc_clk(clk, cstate, 0x0e, nv_clk_src_vdec))) + return ret; + + return 0; +} + +static void +gf100_clk_prog_0(struct gf100_clk *clk, int idx) +{ + struct gf100_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + if (idx < 7 && !info->ssel) { + nvkm_mask(device, 0x1371d0 + (idx * 0x04), 0x80003f3f, info->ddiv); + nvkm_wr32(device, 0x137160 + (idx * 0x04), info->dsrc); + } +} + +static void +gf100_clk_prog_1(struct gf100_clk *clk, int idx) +{ + struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, 0x137100, (1 << idx), 0x00000000); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x137100) & (1 << idx))) + break; + ); +} + +static void +gf100_clk_prog_2(struct gf100_clk *clk, int idx) +{ + struct gf100_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + const u32 addr = 0x137000 + (idx * 0x20); + if (idx <= 7) { + nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000000); + nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000000); + if (info->coef) { + nvkm_wr32(device, addr + 0x04, info->coef); + nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000001); + + /* Test PLL lock */ + nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000000); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, addr + 0x00) & 0x00020000) + break; + ); + nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000010); + + /* Enable sync mode */ + nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000004); + } + } +} + +static void +gf100_clk_prog_3(struct gf100_clk *clk, int idx) +{ + struct gf100_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + if (info->ssel) { + nvkm_mask(device, 0x137100, (1 << idx), info->ssel); + nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x137100) & (1 << idx); + if (tmp == info->ssel) + break; + ); + } +} + +static void +gf100_clk_prog_4(struct gf100_clk *clk, int idx) +{ + struct gf100_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, 0x137250 + (idx * 0x04), 0x00003f3f, info->mdiv); +} + +static int +gf100_clk_prog(struct nvkm_clk *base) +{ + struct gf100_clk *clk = gf100_clk(base); + struct { + void (*exec)(struct gf100_clk *, int); + } stage[] = { + { gf100_clk_prog_0 }, /* div programming */ + { gf100_clk_prog_1 }, /* select div mode */ + { gf100_clk_prog_2 }, /* (maybe) program pll */ + { gf100_clk_prog_3 }, /* (maybe) select pll mode */ + { gf100_clk_prog_4 }, /* final divider */ + }; + int i, j; + + for (i = 0; i < ARRAY_SIZE(stage); i++) { + for (j = 0; j < ARRAY_SIZE(clk->eng); j++) { + if (!clk->eng[j].freq) + continue; + stage[i].exec(clk, j); + } + } + + return 0; +} + +static void +gf100_clk_tidy(struct nvkm_clk *base) +{ + struct gf100_clk *clk = gf100_clk(base); + memset(clk->eng, 0x00, sizeof(clk->eng)); +} + +static const struct nvkm_clk_func +gf100_clk = { + .read = gf100_clk_read, + .calc = gf100_clk_calc, + .prog = gf100_clk_prog, + .tidy = gf100_clk_tidy, + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_href , 0xff }, + { nv_clk_src_hubk06 , 0x00 }, + { nv_clk_src_hubk01 , 0x01 }, + { nv_clk_src_copy , 0x02 }, + { nv_clk_src_gpc , 0x03, NVKM_CLK_DOM_FLAG_VPSTATE, "core", 2000 }, + { nv_clk_src_rop , 0x04 }, + { nv_clk_src_mem , 0x05, 0, "memory", 1000 }, + { nv_clk_src_vdec , 0x06 }, + { nv_clk_src_pmu , 0x0a }, + { nv_clk_src_hubk07 , 0x0b }, + { nv_clk_src_max } + } +}; + +int +gf100_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct gf100_clk *clk; + + if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) + return -ENOMEM; + *pclk = &clk->base; + + return nvkm_clk_ctor(&gf100_clk, device, type, inst, false, &clk->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c new file mode 100644 index 000000000..0d8e2ddcc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c @@ -0,0 +1,517 @@ +/* + * 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 + */ +#define gk104_clk(p) container_of((p), struct gk104_clk, base) +#include "priv.h" +#include "pll.h" + +#include +#include +#include + +struct gk104_clk_info { + u32 freq; + u32 ssel; + u32 mdiv; + u32 dsrc; + u32 ddiv; + u32 coef; +}; + +struct gk104_clk { + struct nvkm_clk base; + struct gk104_clk_info eng[16]; +}; + +static u32 read_div(struct gk104_clk *, int, u32, u32); +static u32 read_pll(struct gk104_clk *, u32); + +static u32 +read_vco(struct gk104_clk *clk, u32 dsrc) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ssrc = nvkm_rd32(device, dsrc); + if (!(ssrc & 0x00000100)) + return read_pll(clk, 0x00e800); + return read_pll(clk, 0x00e820); +} + +static u32 +read_pll(struct gk104_clk *clk, u32 pll) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ctrl = nvkm_rd32(device, pll + 0x00); + u32 coef = nvkm_rd32(device, pll + 0x04); + u32 P = (coef & 0x003f0000) >> 16; + u32 N = (coef & 0x0000ff00) >> 8; + u32 M = (coef & 0x000000ff) >> 0; + u32 sclk; + u16 fN = 0xf000; + + if (!(ctrl & 0x00000001)) + return 0; + + switch (pll) { + case 0x00e800: + case 0x00e820: + sclk = device->crystal; + P = 1; + break; + case 0x132000: + sclk = read_pll(clk, 0x132020); + P = (coef & 0x10000000) ? 2 : 1; + break; + case 0x132020: + sclk = read_div(clk, 0, 0x137320, 0x137330); + fN = nvkm_rd32(device, pll + 0x10) >> 16; + break; + case 0x137000: + case 0x137020: + case 0x137040: + case 0x1370e0: + sclk = read_div(clk, (pll & 0xff) / 0x20, 0x137120, 0x137140); + break; + default: + return 0; + } + + if (P == 0) + P = 1; + + sclk = (sclk * N) + (((u16)(fN + 4096) * sclk) >> 13); + return sclk / (M * P); +} + +static u32 +read_div(struct gk104_clk *clk, int doff, u32 dsrc, u32 dctl) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ssrc = nvkm_rd32(device, dsrc + (doff * 4)); + u32 sctl = nvkm_rd32(device, dctl + (doff * 4)); + + switch (ssrc & 0x00000003) { + case 0: + if ((ssrc & 0x00030000) != 0x00030000) + return device->crystal; + return 108000; + case 2: + return 100000; + case 3: + if (sctl & 0x80000000) { + u32 sclk = read_vco(clk, dsrc + (doff * 4)); + u32 sdiv = (sctl & 0x0000003f) + 2; + return (sclk * 2) / sdiv; + } + + return read_vco(clk, dsrc + (doff * 4)); + default: + return 0; + } +} + +static u32 +read_mem(struct gk104_clk *clk) +{ + struct nvkm_device *device = clk->base.subdev.device; + switch (nvkm_rd32(device, 0x1373f4) & 0x0000000f) { + case 1: return read_pll(clk, 0x132020); + case 2: return read_pll(clk, 0x132000); + default: + return 0; + } +} + +static u32 +read_clk(struct gk104_clk *clk, int idx) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 sctl = nvkm_rd32(device, 0x137250 + (idx * 4)); + u32 sclk, sdiv; + + if (idx < 7) { + u32 ssel = nvkm_rd32(device, 0x137100); + if (ssel & (1 << idx)) { + sclk = read_pll(clk, 0x137000 + (idx * 0x20)); + sdiv = 1; + } else { + sclk = read_div(clk, idx, 0x137160, 0x1371d0); + sdiv = 0; + } + } else { + u32 ssrc = nvkm_rd32(device, 0x137160 + (idx * 0x04)); + if ((ssrc & 0x00000003) == 0x00000003) { + sclk = read_div(clk, idx, 0x137160, 0x1371d0); + if (ssrc & 0x00000100) { + if (ssrc & 0x40000000) + sclk = read_pll(clk, 0x1370e0); + sdiv = 1; + } else { + sdiv = 0; + } + } else { + sclk = read_div(clk, idx, 0x137160, 0x1371d0); + sdiv = 0; + } + } + + if (sctl & 0x80000000) { + if (sdiv) + sdiv = ((sctl & 0x00003f00) >> 8) + 2; + else + sdiv = ((sctl & 0x0000003f) >> 0) + 2; + return (sclk * 2) / sdiv; + } + + return sclk; +} + +static int +gk104_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct gk104_clk *clk = gk104_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + + switch (src) { + case nv_clk_src_crystal: + return device->crystal; + case nv_clk_src_href: + return 100000; + case nv_clk_src_mem: + return read_mem(clk); + case nv_clk_src_gpc: + return read_clk(clk, 0x00); + case nv_clk_src_rop: + return read_clk(clk, 0x01); + case nv_clk_src_hubk07: + return read_clk(clk, 0x02); + case nv_clk_src_hubk06: + return read_clk(clk, 0x07); + case nv_clk_src_hubk01: + return read_clk(clk, 0x08); + case nv_clk_src_pmu: + return read_clk(clk, 0x0c); + case nv_clk_src_vdec: + return read_clk(clk, 0x0e); + default: + nvkm_error(subdev, "invalid clock source %d\n", src); + return -EINVAL; + } +} + +static u32 +calc_div(struct gk104_clk *clk, int idx, u32 ref, u32 freq, u32 *ddiv) +{ + u32 div = min((ref * 2) / freq, (u32)65); + if (div < 2) + div = 2; + + *ddiv = div - 2; + return (ref * 2) / div; +} + +static u32 +calc_src(struct gk104_clk *clk, int idx, u32 freq, u32 *dsrc, u32 *ddiv) +{ + u32 sclk; + + /* use one of the fixed frequencies if possible */ + *ddiv = 0x00000000; + switch (freq) { + case 27000: + case 108000: + *dsrc = 0x00000000; + if (freq == 108000) + *dsrc |= 0x00030000; + return freq; + case 100000: + *dsrc = 0x00000002; + return freq; + default: + *dsrc = 0x00000003; + break; + } + + /* otherwise, calculate the closest divider */ + sclk = read_vco(clk, 0x137160 + (idx * 4)); + if (idx < 7) + sclk = calc_div(clk, idx, sclk, freq, ddiv); + return sclk; +} + +static u32 +calc_pll(struct gk104_clk *clk, int idx, u32 freq, u32 *coef) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_pll limits; + int N, M, P, ret; + + ret = nvbios_pll_parse(bios, 0x137000 + (idx * 0x20), &limits); + if (ret) + return 0; + + limits.refclk = read_div(clk, idx, 0x137120, 0x137140); + if (!limits.refclk) + return 0; + + ret = gt215_pll_calc(subdev, &limits, freq, &N, NULL, &M, &P); + if (ret <= 0) + return 0; + + *coef = (P << 16) | (N << 8) | M; + return ret; +} + +static int +calc_clk(struct gk104_clk *clk, + struct nvkm_cstate *cstate, int idx, int dom) +{ + struct gk104_clk_info *info = &clk->eng[idx]; + u32 freq = cstate->domain[dom]; + u32 src0, div0, div1D, div1P = 0; + u32 clk0, clk1 = 0; + + /* invalid clock domain */ + if (!freq) + return 0; + + /* first possible path, using only dividers */ + clk0 = calc_src(clk, idx, freq, &src0, &div0); + clk0 = calc_div(clk, idx, clk0, freq, &div1D); + + /* see if we can get any closer using PLLs */ + if (clk0 != freq && (0x0000ff87 & (1 << idx))) { + if (idx <= 7) + clk1 = calc_pll(clk, idx, freq, &info->coef); + else + clk1 = cstate->domain[nv_clk_src_hubk06]; + clk1 = calc_div(clk, idx, clk1, freq, &div1P); + } + + /* select the method which gets closest to target freq */ + if (abs((int)freq - clk0) <= abs((int)freq - clk1)) { + info->dsrc = src0; + if (div0) { + info->ddiv |= 0x80000000; + info->ddiv |= div0; + } + if (div1D) { + info->mdiv |= 0x80000000; + info->mdiv |= div1D; + } + info->ssel = 0; + info->freq = clk0; + } else { + if (div1P) { + info->mdiv |= 0x80000000; + info->mdiv |= div1P << 8; + } + info->ssel = (1 << idx); + info->dsrc = 0x40000100; + info->freq = clk1; + } + + return 0; +} + +static int +gk104_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct gk104_clk *clk = gk104_clk(base); + int ret; + + if ((ret = calc_clk(clk, cstate, 0x00, nv_clk_src_gpc)) || + (ret = calc_clk(clk, cstate, 0x01, nv_clk_src_rop)) || + (ret = calc_clk(clk, cstate, 0x02, nv_clk_src_hubk07)) || + (ret = calc_clk(clk, cstate, 0x07, nv_clk_src_hubk06)) || + (ret = calc_clk(clk, cstate, 0x08, nv_clk_src_hubk01)) || + (ret = calc_clk(clk, cstate, 0x0c, nv_clk_src_pmu)) || + (ret = calc_clk(clk, cstate, 0x0e, nv_clk_src_vdec))) + return ret; + + return 0; +} + +static void +gk104_clk_prog_0(struct gk104_clk *clk, int idx) +{ + struct gk104_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + if (!info->ssel) { + nvkm_mask(device, 0x1371d0 + (idx * 0x04), 0x8000003f, info->ddiv); + nvkm_wr32(device, 0x137160 + (idx * 0x04), info->dsrc); + } +} + +static void +gk104_clk_prog_1_0(struct gk104_clk *clk, int idx) +{ + struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, 0x137100, (1 << idx), 0x00000000); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x137100) & (1 << idx))) + break; + ); +} + +static void +gk104_clk_prog_1_1(struct gk104_clk *clk, int idx) +{ + struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, 0x137160 + (idx * 0x04), 0x00000100, 0x00000000); +} + +static void +gk104_clk_prog_2(struct gk104_clk *clk, int idx) +{ + struct gk104_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + const u32 addr = 0x137000 + (idx * 0x20); + nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000000); + nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000000); + if (info->coef) { + nvkm_wr32(device, addr + 0x04, info->coef); + nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000001); + + /* Test PLL lock */ + nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000000); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, addr + 0x00) & 0x00020000) + break; + ); + nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000010); + + /* Enable sync mode */ + nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000004); + } +} + +static void +gk104_clk_prog_3(struct gk104_clk *clk, int idx) +{ + struct gk104_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + if (info->ssel) + nvkm_mask(device, 0x137250 + (idx * 0x04), 0x00003f00, info->mdiv); + else + nvkm_mask(device, 0x137250 + (idx * 0x04), 0x0000003f, info->mdiv); +} + +static void +gk104_clk_prog_4_0(struct gk104_clk *clk, int idx) +{ + struct gk104_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + if (info->ssel) { + nvkm_mask(device, 0x137100, (1 << idx), info->ssel); + nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x137100) & (1 << idx); + if (tmp == info->ssel) + break; + ); + } +} + +static void +gk104_clk_prog_4_1(struct gk104_clk *clk, int idx) +{ + struct gk104_clk_info *info = &clk->eng[idx]; + struct nvkm_device *device = clk->base.subdev.device; + if (info->ssel) { + nvkm_mask(device, 0x137160 + (idx * 0x04), 0x40000000, 0x40000000); + nvkm_mask(device, 0x137160 + (idx * 0x04), 0x00000100, 0x00000100); + } +} + +static int +gk104_clk_prog(struct nvkm_clk *base) +{ + struct gk104_clk *clk = gk104_clk(base); + struct { + u32 mask; + void (*exec)(struct gk104_clk *, int); + } stage[] = { + { 0x007f, gk104_clk_prog_0 }, /* div programming */ + { 0x007f, gk104_clk_prog_1_0 }, /* select div mode */ + { 0xff80, gk104_clk_prog_1_1 }, + { 0x00ff, gk104_clk_prog_2 }, /* (maybe) program pll */ + { 0xff80, gk104_clk_prog_3 }, /* final divider */ + { 0x007f, gk104_clk_prog_4_0 }, /* (maybe) select pll mode */ + { 0xff80, gk104_clk_prog_4_1 }, + }; + int i, j; + + for (i = 0; i < ARRAY_SIZE(stage); i++) { + for (j = 0; j < ARRAY_SIZE(clk->eng); j++) { + if (!(stage[i].mask & (1 << j))) + continue; + if (!clk->eng[j].freq) + continue; + stage[i].exec(clk, j); + } + } + + return 0; +} + +static void +gk104_clk_tidy(struct nvkm_clk *base) +{ + struct gk104_clk *clk = gk104_clk(base); + memset(clk->eng, 0x00, sizeof(clk->eng)); +} + +static const struct nvkm_clk_func +gk104_clk = { + .read = gk104_clk_read, + .calc = gk104_clk_calc, + .prog = gk104_clk_prog, + .tidy = gk104_clk_tidy, + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_href , 0xff }, + { nv_clk_src_gpc , 0x00, NVKM_CLK_DOM_FLAG_CORE | NVKM_CLK_DOM_FLAG_VPSTATE, "core", 2000 }, + { nv_clk_src_hubk07 , 0x01, NVKM_CLK_DOM_FLAG_CORE }, + { nv_clk_src_rop , 0x02, NVKM_CLK_DOM_FLAG_CORE }, + { nv_clk_src_mem , 0x03, 0, "memory", 500 }, + { nv_clk_src_hubk06 , 0x04, NVKM_CLK_DOM_FLAG_CORE }, + { nv_clk_src_hubk01 , 0x05 }, + { nv_clk_src_vdec , 0x06 }, + { nv_clk_src_pmu , 0x07 }, + { nv_clk_src_max } + } +}; + +int +gk104_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct gk104_clk *clk; + + if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) + return -ENOMEM; + *pclk = &clk->base; + + return nvkm_clk_ctor(&gk104_clk, device, type, inst, true, &clk->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c new file mode 100644 index 000000000..d573fb091 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Shamelessly ripped off from ChromeOS's gk20a/clk_pllg.c + * + */ +#include "priv.h" +#include "gk20a.h" + +#include +#include + +static const u8 _pl_to_div[] = { +/* PL: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ +/* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32, +}; + +static u32 pl_to_div(u32 pl) +{ + if (pl >= ARRAY_SIZE(_pl_to_div)) + return 1; + + return _pl_to_div[pl]; +} + +static u32 div_to_pl(u32 div) +{ + u32 pl; + + for (pl = 0; pl < ARRAY_SIZE(_pl_to_div) - 1; pl++) { + if (_pl_to_div[pl] >= div) + return pl; + } + + return ARRAY_SIZE(_pl_to_div) - 1; +} + +static const struct gk20a_clk_pllg_params gk20a_pllg_params = { + .min_vco = 1000000, .max_vco = 2064000, + .min_u = 12000, .max_u = 38000, + .min_m = 1, .max_m = 255, + .min_n = 8, .max_n = 255, + .min_pl = 1, .max_pl = 32, +}; + +void +gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 val; + + val = nvkm_rd32(device, GPCPLL_COEFF); + pll->m = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); + pll->n = (val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH); + pll->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); +} + +void +gk20a_pllg_write_mnp(struct gk20a_clk *clk, const struct gk20a_pll *pll) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 val; + + val = (pll->m & MASK(GPCPLL_COEFF_M_WIDTH)) << GPCPLL_COEFF_M_SHIFT; + val |= (pll->n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT; + val |= (pll->pl & MASK(GPCPLL_COEFF_P_WIDTH)) << GPCPLL_COEFF_P_SHIFT; + nvkm_wr32(device, GPCPLL_COEFF, val); +} + +u32 +gk20a_pllg_calc_rate(struct gk20a_clk *clk, struct gk20a_pll *pll) +{ + u32 rate; + u32 divider; + + rate = clk->parent_rate * pll->n; + divider = pll->m * clk->pl_to_div(pll->pl); + + return rate / divider / 2; +} + +int +gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate, + struct gk20a_pll *pll) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + u32 target_clk_f, ref_clk_f, target_freq; + u32 min_vco_f, max_vco_f; + u32 low_pl, high_pl, best_pl; + u32 target_vco_f; + u32 best_m, best_n; + u32 best_delta = ~0; + u32 pl; + + target_clk_f = rate * 2 / KHZ; + ref_clk_f = clk->parent_rate / KHZ; + + target_vco_f = target_clk_f + target_clk_f / 50; + max_vco_f = max(clk->params->max_vco, target_vco_f); + min_vco_f = clk->params->min_vco; + best_m = clk->params->max_m; + best_n = clk->params->min_n; + best_pl = clk->params->min_pl; + + /* min_pl <= high_pl <= max_pl */ + high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f; + high_pl = min(high_pl, clk->params->max_pl); + high_pl = max(high_pl, clk->params->min_pl); + high_pl = clk->div_to_pl(high_pl); + + /* min_pl <= low_pl <= max_pl */ + low_pl = min_vco_f / target_vco_f; + low_pl = min(low_pl, clk->params->max_pl); + low_pl = max(low_pl, clk->params->min_pl); + low_pl = clk->div_to_pl(low_pl); + + nvkm_debug(subdev, "low_PL %d(div%d), high_PL %d(div%d)", low_pl, + clk->pl_to_div(low_pl), high_pl, clk->pl_to_div(high_pl)); + + /* Select lowest possible VCO */ + for (pl = low_pl; pl <= high_pl; pl++) { + u32 m, n, n2; + + target_vco_f = target_clk_f * clk->pl_to_div(pl); + + for (m = clk->params->min_m; m <= clk->params->max_m; m++) { + u32 u_f = ref_clk_f / m; + + if (u_f < clk->params->min_u) + break; + if (u_f > clk->params->max_u) + continue; + + n = (target_vco_f * m) / ref_clk_f; + n2 = ((target_vco_f * m) + (ref_clk_f - 1)) / ref_clk_f; + + if (n > clk->params->max_n) + break; + + for (; n <= n2; n++) { + u32 vco_f; + + if (n < clk->params->min_n) + continue; + if (n > clk->params->max_n) + break; + + vco_f = ref_clk_f * n / m; + + if (vco_f >= min_vco_f && vco_f <= max_vco_f) { + u32 delta, lwv; + + lwv = (vco_f + (clk->pl_to_div(pl) / 2)) + / clk->pl_to_div(pl); + delta = abs(lwv - target_clk_f); + + if (delta < best_delta) { + best_delta = delta; + best_m = m; + best_n = n; + best_pl = pl; + + if (best_delta == 0) + goto found_match; + } + } + } + } + } + +found_match: + WARN_ON(best_delta == ~0); + + if (best_delta != 0) + nvkm_debug(subdev, + "no best match for target @ %dMHz on gpc_pll", + target_clk_f / KHZ); + + pll->m = best_m; + pll->n = best_n; + pll->pl = best_pl; + + target_freq = gk20a_pllg_calc_rate(clk, pll); + + nvkm_debug(subdev, + "actual target freq %d KHz, M %d, N %d, PL %d(div%d)\n", + target_freq / KHZ, pll->m, pll->n, pll->pl, + clk->pl_to_div(pll->pl)); + return 0; +} + +static int +gk20a_pllg_slide(struct gk20a_clk *clk, u32 n) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + struct gk20a_pll pll; + int ret = 0; + + /* get old coefficients */ + gk20a_pllg_read_mnp(clk, &pll); + /* do nothing if NDIV is the same */ + if (n == pll.n) + return 0; + + /* pll slowdown mode */ + nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT), + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT)); + + /* new ndiv ready for ramp */ + pll.n = n; + udelay(1); + gk20a_pllg_write_mnp(clk, &pll); + + /* dynamic ramp to new ndiv */ + udelay(1); + nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), + BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT)); + + /* wait for ramping to complete */ + if (nvkm_wait_usec(device, 500, GPC_BCAST_NDIV_SLOWDOWN_DEBUG, + GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK, + GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK) < 0) + ret = -ETIMEDOUT; + + /* exit slowdown mode */ + nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) | + BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0); + nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN); + + return ret; +} + +static int +gk20a_pllg_enable(struct gk20a_clk *clk) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 val; + + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE); + nvkm_rd32(device, GPCPLL_CFG); + + /* enable lock detection */ + val = nvkm_rd32(device, GPCPLL_CFG); + if (val & GPCPLL_CFG_LOCK_DET_OFF) { + val &= ~GPCPLL_CFG_LOCK_DET_OFF; + nvkm_wr32(device, GPCPLL_CFG, val); + } + + /* wait for lock */ + if (nvkm_wait_usec(device, 300, GPCPLL_CFG, GPCPLL_CFG_LOCK, + GPCPLL_CFG_LOCK) < 0) + return -ETIMEDOUT; + + /* switch to VCO mode */ + nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), + BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); + + return 0; +} + +static void +gk20a_pllg_disable(struct gk20a_clk *clk) +{ + struct nvkm_device *device = clk->base.subdev.device; + + /* put PLL in bypass before disabling it */ + nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0); + + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0); + nvkm_rd32(device, GPCPLL_CFG); +} + +static int +gk20a_pllg_program_mnp(struct gk20a_clk *clk, const struct gk20a_pll *pll) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + struct gk20a_pll cur_pll; + int ret; + + gk20a_pllg_read_mnp(clk, &cur_pll); + + /* split VCO-to-bypass jump in half by setting out divider 1:2 */ + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT); + /* Intentional 2nd write to assure linear divider operation */ + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT); + nvkm_rd32(device, GPC2CLK_OUT); + udelay(2); + + gk20a_pllg_disable(clk); + + gk20a_pllg_write_mnp(clk, pll); + + ret = gk20a_pllg_enable(clk); + if (ret) + return ret; + + /* restore out divider 1:1 */ + udelay(2); + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT); + /* Intentional 2nd write to assure linear divider operation */ + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT); + nvkm_rd32(device, GPC2CLK_OUT); + + return 0; +} + +static int +gk20a_pllg_program_mnp_slide(struct gk20a_clk *clk, const struct gk20a_pll *pll) +{ + struct gk20a_pll cur_pll; + int ret; + + if (gk20a_pllg_is_enabled(clk)) { + gk20a_pllg_read_mnp(clk, &cur_pll); + + /* just do NDIV slide if there is no change to M and PL */ + if (pll->m == cur_pll.m && pll->pl == cur_pll.pl) + return gk20a_pllg_slide(clk, pll->n); + + /* slide down to current NDIV_LO */ + cur_pll.n = gk20a_pllg_n_lo(clk, &cur_pll); + ret = gk20a_pllg_slide(clk, cur_pll.n); + if (ret) + return ret; + } + + /* program MNP with the new clock parameters and new NDIV_LO */ + cur_pll = *pll; + cur_pll.n = gk20a_pllg_n_lo(clk, &cur_pll); + ret = gk20a_pllg_program_mnp(clk, &cur_pll); + if (ret) + return ret; + + /* slide up to new NDIV */ + return gk20a_pllg_slide(clk, pll->n); +} + +static struct nvkm_pstate +gk20a_pstates[] = { + { + .base = { + .domain[nv_clk_src_gpc] = 72000, + .voltage = 0, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 108000, + .voltage = 1, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 180000, + .voltage = 2, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 252000, + .voltage = 3, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 324000, + .voltage = 4, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 396000, + .voltage = 5, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 468000, + .voltage = 6, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 540000, + .voltage = 7, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 612000, + .voltage = 8, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 648000, + .voltage = 9, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 684000, + .voltage = 10, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 708000, + .voltage = 11, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 756000, + .voltage = 12, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 804000, + .voltage = 13, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 852000, + .voltage = 14, + }, + }, +}; + +int +gk20a_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct gk20a_clk *clk = gk20a_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + struct gk20a_pll pll; + + switch (src) { + case nv_clk_src_crystal: + return device->crystal; + case nv_clk_src_gpc: + gk20a_pllg_read_mnp(clk, &pll); + return gk20a_pllg_calc_rate(clk, &pll) / GK20A_CLK_GPC_MDIV; + default: + nvkm_error(subdev, "invalid clock source %d\n", src); + return -EINVAL; + } +} + +int +gk20a_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct gk20a_clk *clk = gk20a_clk(base); + + return gk20a_pllg_calc_mnp(clk, cstate->domain[nv_clk_src_gpc] * + GK20A_CLK_GPC_MDIV, &clk->pll); +} + +int +gk20a_clk_prog(struct nvkm_clk *base) +{ + struct gk20a_clk *clk = gk20a_clk(base); + int ret; + + ret = gk20a_pllg_program_mnp_slide(clk, &clk->pll); + if (ret) + ret = gk20a_pllg_program_mnp(clk, &clk->pll); + + return ret; +} + +void +gk20a_clk_tidy(struct nvkm_clk *base) +{ +} + +int +gk20a_clk_setup_slide(struct gk20a_clk *clk) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 step_a, step_b; + + switch (clk->parent_rate) { + case 12000000: + case 12800000: + case 13000000: + step_a = 0x2b; + step_b = 0x0b; + break; + case 19200000: + step_a = 0x12; + step_b = 0x08; + break; + case 38400000: + step_a = 0x04; + step_b = 0x05; + break; + default: + nvkm_error(subdev, "invalid parent clock rate %u KHz", + clk->parent_rate / KHZ); + return -EINVAL; + } + + nvkm_mask(device, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT, + step_a << GPCPLL_CFG2_PLL_STEPA_SHIFT); + nvkm_mask(device, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT, + step_b << GPCPLL_CFG3_PLL_STEPB_SHIFT); + + return 0; +} + +void +gk20a_clk_fini(struct nvkm_clk *base) +{ + struct nvkm_device *device = base->subdev.device; + struct gk20a_clk *clk = gk20a_clk(base); + + /* slide to VCO min */ + if (gk20a_pllg_is_enabled(clk)) { + struct gk20a_pll pll; + u32 n_lo; + + gk20a_pllg_read_mnp(clk, &pll); + n_lo = gk20a_pllg_n_lo(clk, &pll); + gk20a_pllg_slide(clk, n_lo); + } + + gk20a_pllg_disable(clk); + + /* set IDDQ */ + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 1); +} + +static int +gk20a_clk_init(struct nvkm_clk *base) +{ + struct gk20a_clk *clk = gk20a_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + int ret; + + /* get out from IDDQ */ + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 0); + nvkm_rd32(device, GPCPLL_CFG); + udelay(5); + + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, + GPC2CLK_OUT_INIT_VAL); + + ret = gk20a_clk_setup_slide(clk); + if (ret) + return ret; + + /* Start with lowest frequency */ + base->func->calc(base, &base->func->pstates[0].base); + ret = base->func->prog(&clk->base); + if (ret) { + nvkm_error(subdev, "cannot initialize clock\n"); + return ret; + } + + return 0; +} + +static const struct nvkm_clk_func +gk20a_clk = { + .init = gk20a_clk_init, + .fini = gk20a_clk_fini, + .read = gk20a_clk_read, + .calc = gk20a_clk_calc, + .prog = gk20a_clk_prog, + .tidy = gk20a_clk_tidy, + .pstates = gk20a_pstates, + .nr_pstates = ARRAY_SIZE(gk20a_pstates), + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV }, + { nv_clk_src_max } + } +}; + +int +gk20a_clk_ctor(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + const struct nvkm_clk_func *func, const struct gk20a_clk_pllg_params *params, + struct gk20a_clk *clk) +{ + struct nvkm_device_tegra *tdev = device->func->tegra(device); + int ret; + int i; + + /* Finish initializing the pstates */ + for (i = 0; i < func->nr_pstates; i++) { + INIT_LIST_HEAD(&func->pstates[i].list); + func->pstates[i].pstate = i + 1; + } + + clk->params = params; + clk->parent_rate = clk_get_rate(tdev->clk); + + ret = nvkm_clk_ctor(func, device, type, inst, true, &clk->base); + if (ret) + return ret; + + nvkm_debug(&clk->base.subdev, "parent clock rate: %d Khz\n", + clk->parent_rate / KHZ); + + return 0; +} + +int +gk20a_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct gk20a_clk *clk; + int ret; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) + return -ENOMEM; + *pclk = &clk->base; + + ret = gk20a_clk_ctor(device, type, inst, &gk20a_clk, &gk20a_pllg_params, clk); + + clk->pl_to_div = pl_to_div; + clk->div_to_pl = div_to_pl; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h new file mode 100644 index 000000000..286413ff4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NVKM_CLK_GK20A_H__ +#define __NVKM_CLK_GK20A_H__ + +#define KHZ (1000) +#define MHZ (KHZ * 1000) + +#define MASK(w) ((1 << (w)) - 1) + +#define GK20A_CLK_GPC_MDIV 1000 + +#define SYS_GPCPLL_CFG_BASE 0x00137000 +#define GPCPLL_CFG (SYS_GPCPLL_CFG_BASE + 0) +#define GPCPLL_CFG_ENABLE BIT(0) +#define GPCPLL_CFG_IDDQ BIT(1) +#define GPCPLL_CFG_LOCK_DET_OFF BIT(4) +#define GPCPLL_CFG_LOCK BIT(17) + +#define GPCPLL_CFG2 (SYS_GPCPLL_CFG_BASE + 0xc) +#define GPCPLL_CFG2_SETUP2_SHIFT 16 +#define GPCPLL_CFG2_PLL_STEPA_SHIFT 24 + +#define GPCPLL_CFG3 (SYS_GPCPLL_CFG_BASE + 0x18) +#define GPCPLL_CFG3_VCO_CTRL_SHIFT 0 +#define GPCPLL_CFG3_VCO_CTRL_WIDTH 9 +#define GPCPLL_CFG3_VCO_CTRL_MASK \ + (MASK(GPCPLL_CFG3_VCO_CTRL_WIDTH) << GPCPLL_CFG3_VCO_CTRL_SHIFT) +#define GPCPLL_CFG3_PLL_STEPB_SHIFT 16 +#define GPCPLL_CFG3_PLL_STEPB_WIDTH 8 + +#define GPCPLL_COEFF (SYS_GPCPLL_CFG_BASE + 4) +#define GPCPLL_COEFF_M_SHIFT 0 +#define GPCPLL_COEFF_M_WIDTH 8 +#define GPCPLL_COEFF_N_SHIFT 8 +#define GPCPLL_COEFF_N_WIDTH 8 +#define GPCPLL_COEFF_N_MASK \ + (MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT) +#define GPCPLL_COEFF_P_SHIFT 16 +#define GPCPLL_COEFF_P_WIDTH 6 + +#define GPCPLL_NDIV_SLOWDOWN (SYS_GPCPLL_CFG_BASE + 0x1c) +#define GPCPLL_NDIV_SLOWDOWN_NDIV_LO_SHIFT 0 +#define GPCPLL_NDIV_SLOWDOWN_NDIV_MID_SHIFT 8 +#define GPCPLL_NDIV_SLOWDOWN_STEP_SIZE_LO2MID_SHIFT 16 +#define GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT 22 +#define GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT 31 + +#define GPC_BCAST_GPCPLL_CFG_BASE 0x00132800 +#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG (GPC_BCAST_GPCPLL_CFG_BASE + 0xa0) +#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT 24 +#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK \ + (0x1 << GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT) + +#define SEL_VCO (SYS_GPCPLL_CFG_BASE + 0x100) +#define SEL_VCO_GPC2CLK_OUT_SHIFT 0 + +#define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250) +#define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1 +#define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31 +#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1 +#define GPC2CLK_OUT_VCODIV_WIDTH 6 +#define GPC2CLK_OUT_VCODIV_SHIFT 8 +#define GPC2CLK_OUT_VCODIV1 0 +#define GPC2CLK_OUT_VCODIV2 2 +#define GPC2CLK_OUT_VCODIV_MASK (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \ + GPC2CLK_OUT_VCODIV_SHIFT) +#define GPC2CLK_OUT_BYPDIV_WIDTH 6 +#define GPC2CLK_OUT_BYPDIV_SHIFT 0 +#define GPC2CLK_OUT_BYPDIV31 0x3c +#define GPC2CLK_OUT_INIT_MASK ((MASK(GPC2CLK_OUT_SDIV14_INDIV4_WIDTH) << \ + GPC2CLK_OUT_SDIV14_INDIV4_SHIFT)\ + | (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << GPC2CLK_OUT_VCODIV_SHIFT)\ + | (MASK(GPC2CLK_OUT_BYPDIV_WIDTH) << GPC2CLK_OUT_BYPDIV_SHIFT)) +#define GPC2CLK_OUT_INIT_VAL ((GPC2CLK_OUT_SDIV14_INDIV4_MODE << \ + GPC2CLK_OUT_SDIV14_INDIV4_SHIFT) \ + | (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT) \ + | (GPC2CLK_OUT_BYPDIV31 << GPC2CLK_OUT_BYPDIV_SHIFT)) + +/* All frequencies in Khz */ +struct gk20a_clk_pllg_params { + u32 min_vco, max_vco; + u32 min_u, max_u; + u32 min_m, max_m; + u32 min_n, max_n; + u32 min_pl, max_pl; +}; + +struct gk20a_pll { + u32 m; + u32 n; + u32 pl; +}; + +struct gk20a_clk { + struct nvkm_clk base; + const struct gk20a_clk_pllg_params *params; + struct gk20a_pll pll; + u32 parent_rate; + + u32 (*div_to_pl)(u32); + u32 (*pl_to_div)(u32); +}; +#define gk20a_clk(p) container_of((p), struct gk20a_clk, base) + +u32 gk20a_pllg_calc_rate(struct gk20a_clk *, struct gk20a_pll *); +int gk20a_pllg_calc_mnp(struct gk20a_clk *, unsigned long, struct gk20a_pll *); +void gk20a_pllg_read_mnp(struct gk20a_clk *, struct gk20a_pll *); +void gk20a_pllg_write_mnp(struct gk20a_clk *, const struct gk20a_pll *); + +static inline bool +gk20a_pllg_is_enabled(struct gk20a_clk *clk) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 val; + + val = nvkm_rd32(device, GPCPLL_CFG); + return val & GPCPLL_CFG_ENABLE; +} + +static inline u32 +gk20a_pllg_n_lo(struct gk20a_clk *clk, struct gk20a_pll *pll) +{ + return DIV_ROUND_UP(pll->m * clk->params->min_vco, + clk->parent_rate / KHZ); +} + +int gk20a_clk_ctor(struct nvkm_device *, enum nvkm_subdev_type, int, const struct nvkm_clk_func *, + const struct gk20a_clk_pllg_params *, struct gk20a_clk *); +void gk20a_clk_fini(struct nvkm_clk *); +int gk20a_clk_read(struct nvkm_clk *, enum nv_clk_src); +int gk20a_clk_calc(struct nvkm_clk *, struct nvkm_cstate *); +int gk20a_clk_prog(struct nvkm_clk *); +void gk20a_clk_tidy(struct nvkm_clk *); + +int gk20a_clk_setup_slide(struct gk20a_clk *); + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c new file mode 100644 index 000000000..7c33542f6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c @@ -0,0 +1,1071 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "priv.h" +#include "gk20a.h" + +#define GPCPLL_CFG_SYNC_MODE BIT(2) + +#define BYPASSCTRL_SYS (SYS_GPCPLL_CFG_BASE + 0x340) +#define BYPASSCTRL_SYS_GPCPLL_SHIFT 0 +#define BYPASSCTRL_SYS_GPCPLL_WIDTH 1 + +#define GPCPLL_CFG2_SDM_DIN_SHIFT 0 +#define GPCPLL_CFG2_SDM_DIN_WIDTH 8 +#define GPCPLL_CFG2_SDM_DIN_MASK \ + (MASK(GPCPLL_CFG2_SDM_DIN_WIDTH) << GPCPLL_CFG2_SDM_DIN_SHIFT) +#define GPCPLL_CFG2_SDM_DIN_NEW_SHIFT 8 +#define GPCPLL_CFG2_SDM_DIN_NEW_WIDTH 15 +#define GPCPLL_CFG2_SDM_DIN_NEW_MASK \ + (MASK(GPCPLL_CFG2_SDM_DIN_NEW_WIDTH) << GPCPLL_CFG2_SDM_DIN_NEW_SHIFT) +#define GPCPLL_CFG2_SETUP2_SHIFT 16 +#define GPCPLL_CFG2_PLL_STEPA_SHIFT 24 + +#define GPCPLL_DVFS0 (SYS_GPCPLL_CFG_BASE + 0x10) +#define GPCPLL_DVFS0_DFS_COEFF_SHIFT 0 +#define GPCPLL_DVFS0_DFS_COEFF_WIDTH 7 +#define GPCPLL_DVFS0_DFS_COEFF_MASK \ + (MASK(GPCPLL_DVFS0_DFS_COEFF_WIDTH) << GPCPLL_DVFS0_DFS_COEFF_SHIFT) +#define GPCPLL_DVFS0_DFS_DET_MAX_SHIFT 8 +#define GPCPLL_DVFS0_DFS_DET_MAX_WIDTH 7 +#define GPCPLL_DVFS0_DFS_DET_MAX_MASK \ + (MASK(GPCPLL_DVFS0_DFS_DET_MAX_WIDTH) << GPCPLL_DVFS0_DFS_DET_MAX_SHIFT) + +#define GPCPLL_DVFS1 (SYS_GPCPLL_CFG_BASE + 0x14) +#define GPCPLL_DVFS1_DFS_EXT_DET_SHIFT 0 +#define GPCPLL_DVFS1_DFS_EXT_DET_WIDTH 7 +#define GPCPLL_DVFS1_DFS_EXT_STRB_SHIFT 7 +#define GPCPLL_DVFS1_DFS_EXT_STRB_WIDTH 1 +#define GPCPLL_DVFS1_DFS_EXT_CAL_SHIFT 8 +#define GPCPLL_DVFS1_DFS_EXT_CAL_WIDTH 7 +#define GPCPLL_DVFS1_DFS_EXT_SEL_SHIFT 15 +#define GPCPLL_DVFS1_DFS_EXT_SEL_WIDTH 1 +#define GPCPLL_DVFS1_DFS_CTRL_SHIFT 16 +#define GPCPLL_DVFS1_DFS_CTRL_WIDTH 12 +#define GPCPLL_DVFS1_EN_SDM_SHIFT 28 +#define GPCPLL_DVFS1_EN_SDM_WIDTH 1 +#define GPCPLL_DVFS1_EN_SDM_BIT BIT(28) +#define GPCPLL_DVFS1_EN_DFS_SHIFT 29 +#define GPCPLL_DVFS1_EN_DFS_WIDTH 1 +#define GPCPLL_DVFS1_EN_DFS_BIT BIT(29) +#define GPCPLL_DVFS1_EN_DFS_CAL_SHIFT 30 +#define GPCPLL_DVFS1_EN_DFS_CAL_WIDTH 1 +#define GPCPLL_DVFS1_EN_DFS_CAL_BIT BIT(30) +#define GPCPLL_DVFS1_DFS_CAL_DONE_SHIFT 31 +#define GPCPLL_DVFS1_DFS_CAL_DONE_WIDTH 1 +#define GPCPLL_DVFS1_DFS_CAL_DONE_BIT BIT(31) + +#define GPC_BCAST_GPCPLL_DVFS2 (GPC_BCAST_GPCPLL_CFG_BASE + 0x20) +#define GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT BIT(16) + +#define GPCPLL_CFG3_PLL_DFS_TESTOUT_SHIFT 24 +#define GPCPLL_CFG3_PLL_DFS_TESTOUT_WIDTH 7 + +#define DFS_DET_RANGE 6 /* -2^6 ... 2^6-1 */ +#define SDM_DIN_RANGE 12 /* -2^12 ... 2^12-1 */ + +struct gm20b_clk_dvfs_params { + s32 coeff_slope; + s32 coeff_offs; + u32 vco_ctrl; +}; + +static const struct gm20b_clk_dvfs_params gm20b_dvfs_params = { + .coeff_slope = -165230, + .coeff_offs = 214007, + .vco_ctrl = 0x7 << 3, +}; + +/* + * base.n is now the *integer* part of the N factor. + * sdm_din contains n's decimal part. + */ +struct gm20b_pll { + struct gk20a_pll base; + u32 sdm_din; +}; + +struct gm20b_clk_dvfs { + u32 dfs_coeff; + s32 dfs_det_max; + s32 dfs_ext_cal; +}; + +struct gm20b_clk { + /* currently applied parameters */ + struct gk20a_clk base; + struct gm20b_clk_dvfs dvfs; + u32 uv; + + /* new parameters to apply */ + struct gk20a_pll new_pll; + struct gm20b_clk_dvfs new_dvfs; + u32 new_uv; + + const struct gm20b_clk_dvfs_params *dvfs_params; + + /* fused parameters */ + s32 uvdet_slope; + s32 uvdet_offs; + + /* safe frequency we can use at minimum voltage */ + u32 safe_fmax_vmin; +}; +#define gm20b_clk(p) container_of((gk20a_clk(p)), struct gm20b_clk, base) + +static u32 pl_to_div(u32 pl) +{ + return pl; +} + +static u32 div_to_pl(u32 div) +{ + return div; +} + +static const struct gk20a_clk_pllg_params gm20b_pllg_params = { + .min_vco = 1300000, .max_vco = 2600000, + .min_u = 12000, .max_u = 38400, + .min_m = 1, .max_m = 255, + .min_n = 8, .max_n = 255, + .min_pl = 1, .max_pl = 31, +}; + +static void +gm20b_pllg_read_mnp(struct gm20b_clk *clk, struct gm20b_pll *pll) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + struct nvkm_device *device = subdev->device; + u32 val; + + gk20a_pllg_read_mnp(&clk->base, &pll->base); + val = nvkm_rd32(device, GPCPLL_CFG2); + pll->sdm_din = (val >> GPCPLL_CFG2_SDM_DIN_SHIFT) & + MASK(GPCPLL_CFG2_SDM_DIN_WIDTH); +} + +static void +gm20b_pllg_write_mnp(struct gm20b_clk *clk, const struct gm20b_pll *pll) +{ + struct nvkm_device *device = clk->base.base.subdev.device; + + nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_MASK, + pll->sdm_din << GPCPLL_CFG2_SDM_DIN_SHIFT); + gk20a_pllg_write_mnp(&clk->base, &pll->base); +} + +/* + * Determine DFS_COEFF for the requested voltage. Always select external + * calibration override equal to the voltage, and set maximum detection + * limit "0" (to make sure that PLL output remains under F/V curve when + * voltage increases). + */ +static void +gm20b_dvfs_calc_det_coeff(struct gm20b_clk *clk, s32 uv, + struct gm20b_clk_dvfs *dvfs) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + const struct gm20b_clk_dvfs_params *p = clk->dvfs_params; + u32 coeff; + /* Work with mv as uv would likely trigger an overflow */ + s32 mv = DIV_ROUND_CLOSEST(uv, 1000); + + /* coeff = slope * voltage + offset */ + coeff = DIV_ROUND_CLOSEST(mv * p->coeff_slope, 1000) + p->coeff_offs; + coeff = DIV_ROUND_CLOSEST(coeff, 1000); + dvfs->dfs_coeff = min_t(u32, coeff, MASK(GPCPLL_DVFS0_DFS_COEFF_WIDTH)); + + dvfs->dfs_ext_cal = DIV_ROUND_CLOSEST(uv - clk->uvdet_offs, + clk->uvdet_slope); + /* should never happen */ + if (abs(dvfs->dfs_ext_cal) >= BIT(DFS_DET_RANGE)) + nvkm_error(subdev, "dfs_ext_cal overflow!\n"); + + dvfs->dfs_det_max = 0; + + nvkm_debug(subdev, "%s uv: %d coeff: %x, ext_cal: %d, det_max: %d\n", + __func__, uv, dvfs->dfs_coeff, dvfs->dfs_ext_cal, + dvfs->dfs_det_max); +} + +/* + * Solve equation for integer and fractional part of the effective NDIV: + * + * n_eff = n_int + 1/2 + (SDM_DIN / 2^(SDM_DIN_RANGE + 1)) + + * (DVFS_COEFF * DVFS_DET_DELTA) / 2^DFS_DET_RANGE + * + * The SDM_DIN LSB is finally shifted out, since it is not accessible by sw. + */ +static void +gm20b_dvfs_calc_ndiv(struct gm20b_clk *clk, u32 n_eff, u32 *n_int, u32 *sdm_din) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + const struct gk20a_clk_pllg_params *p = clk->base.params; + u32 n; + s32 det_delta; + u32 rem, rem_range; + + /* calculate current ext_cal and subtract previous one */ + det_delta = DIV_ROUND_CLOSEST(((s32)clk->uv) - clk->uvdet_offs, + clk->uvdet_slope); + det_delta -= clk->dvfs.dfs_ext_cal; + det_delta = min(det_delta, clk->dvfs.dfs_det_max); + det_delta *= clk->dvfs.dfs_coeff; + + /* integer part of n */ + n = (n_eff << DFS_DET_RANGE) - det_delta; + /* should never happen! */ + if (n <= 0) { + nvkm_error(subdev, "ndiv <= 0 - setting to 1...\n"); + n = 1 << DFS_DET_RANGE; + } + if (n >> DFS_DET_RANGE > p->max_n) { + nvkm_error(subdev, "ndiv > max_n - setting to max_n...\n"); + n = p->max_n << DFS_DET_RANGE; + } + *n_int = n >> DFS_DET_RANGE; + + /* fractional part of n */ + rem = ((u32)n) & MASK(DFS_DET_RANGE); + rem_range = SDM_DIN_RANGE + 1 - DFS_DET_RANGE; + /* subtract 2^SDM_DIN_RANGE to account for the 1/2 of the equation */ + rem = (rem << rem_range) - BIT(SDM_DIN_RANGE); + /* lose 8 LSB and clip - sdm_din only keeps the most significant byte */ + *sdm_din = (rem >> BITS_PER_BYTE) & MASK(GPCPLL_CFG2_SDM_DIN_WIDTH); + + nvkm_debug(subdev, "%s n_eff: %d, n_int: %d, sdm_din: %d\n", __func__, + n_eff, *n_int, *sdm_din); +} + +static int +gm20b_pllg_slide(struct gm20b_clk *clk, u32 n) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + struct nvkm_device *device = subdev->device; + struct gm20b_pll pll; + u32 n_int, sdm_din; + int ret = 0; + + /* calculate the new n_int/sdm_din for this n/uv */ + gm20b_dvfs_calc_ndiv(clk, n, &n_int, &sdm_din); + + /* get old coefficients */ + gm20b_pllg_read_mnp(clk, &pll); + /* do nothing if NDIV is the same */ + if (n_int == pll.base.n && sdm_din == pll.sdm_din) + return 0; + + /* pll slowdown mode */ + nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT), + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT)); + + /* new ndiv ready for ramp */ + /* in DVFS mode SDM is updated via "new" field */ + nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_NEW_MASK, + sdm_din << GPCPLL_CFG2_SDM_DIN_NEW_SHIFT); + pll.base.n = n_int; + udelay(1); + gk20a_pllg_write_mnp(&clk->base, &pll.base); + + /* dynamic ramp to new ndiv */ + udelay(1); + nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), + BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT)); + + /* wait for ramping to complete */ + if (nvkm_wait_usec(device, 500, GPC_BCAST_NDIV_SLOWDOWN_DEBUG, + GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK, + GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK) < 0) + ret = -ETIMEDOUT; + + /* in DVFS mode complete SDM update */ + nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_MASK, + sdm_din << GPCPLL_CFG2_SDM_DIN_SHIFT); + + /* exit slowdown mode */ + nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, + BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) | + BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0); + nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN); + + return ret; +} + +static int +gm20b_pllg_enable(struct gm20b_clk *clk) +{ + struct nvkm_device *device = clk->base.base.subdev.device; + + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE); + nvkm_rd32(device, GPCPLL_CFG); + + /* In DVFS mode lock cannot be used - so just delay */ + udelay(40); + + /* set SYNC_MODE for glitchless switch out of bypass */ + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_SYNC_MODE, + GPCPLL_CFG_SYNC_MODE); + nvkm_rd32(device, GPCPLL_CFG); + + /* switch to VCO mode */ + nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), + BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); + + return 0; +} + +static void +gm20b_pllg_disable(struct gm20b_clk *clk) +{ + struct nvkm_device *device = clk->base.base.subdev.device; + + /* put PLL in bypass before disabling it */ + nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0); + + /* clear SYNC_MODE before disabling PLL */ + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_SYNC_MODE, 0); + + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0); + nvkm_rd32(device, GPCPLL_CFG); +} + +static int +gm20b_pllg_program_mnp(struct gm20b_clk *clk, const struct gk20a_pll *pll) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + struct nvkm_device *device = subdev->device; + struct gm20b_pll cur_pll; + u32 n_int, sdm_din; + /* if we only change pdiv, we can do a glitchless transition */ + bool pdiv_only; + int ret; + + gm20b_dvfs_calc_ndiv(clk, pll->n, &n_int, &sdm_din); + gm20b_pllg_read_mnp(clk, &cur_pll); + pdiv_only = cur_pll.base.n == n_int && cur_pll.sdm_din == sdm_din && + cur_pll.base.m == pll->m; + + /* need full sequence if clock not enabled yet */ + if (!gk20a_pllg_is_enabled(&clk->base)) + pdiv_only = false; + + /* split VCO-to-bypass jump in half by setting out divider 1:2 */ + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT); + /* Intentional 2nd write to assure linear divider operation */ + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT); + nvkm_rd32(device, GPC2CLK_OUT); + udelay(2); + + if (pdiv_only) { + u32 old = cur_pll.base.pl; + u32 new = pll->pl; + + /* + * we can do a glitchless transition only if the old and new PL + * parameters share at least one bit set to 1. If this is not + * the case, calculate and program an interim PL that will allow + * us to respect that rule. + */ + if ((old & new) == 0) { + cur_pll.base.pl = min(old | BIT(ffs(new) - 1), + new | BIT(ffs(old) - 1)); + gk20a_pllg_write_mnp(&clk->base, &cur_pll.base); + } + + cur_pll.base.pl = new; + gk20a_pllg_write_mnp(&clk->base, &cur_pll.base); + } else { + /* disable before programming if more than pdiv changes */ + gm20b_pllg_disable(clk); + + cur_pll.base = *pll; + cur_pll.base.n = n_int; + cur_pll.sdm_din = sdm_din; + gm20b_pllg_write_mnp(clk, &cur_pll); + + ret = gm20b_pllg_enable(clk); + if (ret) + return ret; + } + + /* restore out divider 1:1 */ + udelay(2); + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT); + /* Intentional 2nd write to assure linear divider operation */ + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, + GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT); + nvkm_rd32(device, GPC2CLK_OUT); + + return 0; +} + +static int +gm20b_pllg_program_mnp_slide(struct gm20b_clk *clk, const struct gk20a_pll *pll) +{ + struct gk20a_pll cur_pll; + int ret; + + if (gk20a_pllg_is_enabled(&clk->base)) { + gk20a_pllg_read_mnp(&clk->base, &cur_pll); + + /* just do NDIV slide if there is no change to M and PL */ + if (pll->m == cur_pll.m && pll->pl == cur_pll.pl) + return gm20b_pllg_slide(clk, pll->n); + + /* slide down to current NDIV_LO */ + cur_pll.n = gk20a_pllg_n_lo(&clk->base, &cur_pll); + ret = gm20b_pllg_slide(clk, cur_pll.n); + if (ret) + return ret; + } + + /* program MNP with the new clock parameters and new NDIV_LO */ + cur_pll = *pll; + cur_pll.n = gk20a_pllg_n_lo(&clk->base, &cur_pll); + ret = gm20b_pllg_program_mnp(clk, &cur_pll); + if (ret) + return ret; + + /* slide up to new NDIV */ + return gm20b_pllg_slide(clk, pll->n); +} + +static int +gm20b_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct gm20b_clk *clk = gm20b_clk(base); + struct nvkm_subdev *subdev = &base->subdev; + struct nvkm_volt *volt = base->subdev.device->volt; + int ret; + + ret = gk20a_pllg_calc_mnp(&clk->base, cstate->domain[nv_clk_src_gpc] * + GK20A_CLK_GPC_MDIV, &clk->new_pll); + if (ret) + return ret; + + clk->new_uv = volt->vid[cstate->voltage].uv; + gm20b_dvfs_calc_det_coeff(clk, clk->new_uv, &clk->new_dvfs); + + nvkm_debug(subdev, "%s uv: %d uv\n", __func__, clk->new_uv); + + return 0; +} + +/* + * Compute PLL parameters that are always safe for the current voltage + */ +static void +gm20b_dvfs_calc_safe_pll(struct gm20b_clk *clk, struct gk20a_pll *pll) +{ + u32 rate = gk20a_pllg_calc_rate(&clk->base, pll) / KHZ; + u32 parent_rate = clk->base.parent_rate / KHZ; + u32 nmin, nsafe; + + /* remove a safe margin of 10% */ + if (rate > clk->safe_fmax_vmin) + rate = rate * (100 - 10) / 100; + + /* gpc2clk */ + rate *= 2; + + nmin = DIV_ROUND_UP(pll->m * clk->base.params->min_vco, parent_rate); + nsafe = pll->m * rate / (clk->base.parent_rate); + + if (nsafe < nmin) { + pll->pl = DIV_ROUND_UP(nmin * parent_rate, pll->m * rate); + nsafe = nmin; + } + + pll->n = nsafe; +} + +static void +gm20b_dvfs_program_coeff(struct gm20b_clk *clk, u32 coeff) +{ + struct nvkm_device *device = clk->base.base.subdev.device; + + /* strobe to read external DFS coefficient */ + nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2, + GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, + GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT); + + nvkm_mask(device, GPCPLL_DVFS0, GPCPLL_DVFS0_DFS_COEFF_MASK, + coeff << GPCPLL_DVFS0_DFS_COEFF_SHIFT); + + udelay(1); + nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2, + GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, 0); +} + +static void +gm20b_dvfs_program_ext_cal(struct gm20b_clk *clk, u32 dfs_det_cal) +{ + struct nvkm_device *device = clk->base.base.subdev.device; + u32 val; + + nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2, MASK(DFS_DET_RANGE + 1), + dfs_det_cal); + udelay(1); + + val = nvkm_rd32(device, GPCPLL_DVFS1); + if (!(val & BIT(25))) { + /* Use external value to overwrite calibration value */ + val |= BIT(25) | BIT(16); + nvkm_wr32(device, GPCPLL_DVFS1, val); + } +} + +static void +gm20b_dvfs_program_dfs_detection(struct gm20b_clk *clk, + struct gm20b_clk_dvfs *dvfs) +{ + struct nvkm_device *device = clk->base.base.subdev.device; + + /* strobe to read external DFS coefficient */ + nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2, + GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, + GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT); + + nvkm_mask(device, GPCPLL_DVFS0, + GPCPLL_DVFS0_DFS_COEFF_MASK | GPCPLL_DVFS0_DFS_DET_MAX_MASK, + dvfs->dfs_coeff << GPCPLL_DVFS0_DFS_COEFF_SHIFT | + dvfs->dfs_det_max << GPCPLL_DVFS0_DFS_DET_MAX_SHIFT); + + udelay(1); + nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2, + GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, 0); + + gm20b_dvfs_program_ext_cal(clk, dvfs->dfs_ext_cal); +} + +static int +gm20b_clk_prog(struct nvkm_clk *base) +{ + struct gm20b_clk *clk = gm20b_clk(base); + u32 cur_freq; + int ret; + + /* No change in DVFS settings? */ + if (clk->uv == clk->new_uv) + goto prog; + + /* + * Interim step for changing DVFS detection settings: low enough + * frequency to be safe at DVFS coeff = 0. + * + * 1. If voltage is increasing: + * - safe frequency target matches the lowest - old - frequency + * - DVFS settings are still old + * - Voltage already increased to new level by volt, but maximum + * detection limit assures PLL output remains under F/V curve + * + * 2. If voltage is decreasing: + * - safe frequency target matches the lowest - new - frequency + * - DVFS settings are still old + * - Voltage is also old, it will be lowered by volt afterwards + * + * Interim step can be skipped if old frequency is below safe minimum, + * i.e., it is low enough to be safe at any voltage in operating range + * with zero DVFS coefficient. + */ + cur_freq = nvkm_clk_read(&clk->base.base, nv_clk_src_gpc); + if (cur_freq > clk->safe_fmax_vmin) { + struct gk20a_pll pll_safe; + + if (clk->uv < clk->new_uv) + /* voltage will raise: safe frequency is current one */ + pll_safe = clk->base.pll; + else + /* voltage will drop: safe frequency is new one */ + pll_safe = clk->new_pll; + + gm20b_dvfs_calc_safe_pll(clk, &pll_safe); + ret = gm20b_pllg_program_mnp_slide(clk, &pll_safe); + if (ret) + return ret; + } + + /* + * DVFS detection settings transition: + * - Set DVFS coefficient zero + * - Set calibration level to new voltage + * - Set DVFS coefficient to match new voltage + */ + gm20b_dvfs_program_coeff(clk, 0); + gm20b_dvfs_program_ext_cal(clk, clk->new_dvfs.dfs_ext_cal); + gm20b_dvfs_program_coeff(clk, clk->new_dvfs.dfs_coeff); + gm20b_dvfs_program_dfs_detection(clk, &clk->new_dvfs); + +prog: + clk->uv = clk->new_uv; + clk->dvfs = clk->new_dvfs; + clk->base.pll = clk->new_pll; + + return gm20b_pllg_program_mnp_slide(clk, &clk->base.pll); +} + +static struct nvkm_pstate +gm20b_pstates[] = { + { + .base = { + .domain[nv_clk_src_gpc] = 76800, + .voltage = 0, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 153600, + .voltage = 1, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 230400, + .voltage = 2, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 307200, + .voltage = 3, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 384000, + .voltage = 4, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 460800, + .voltage = 5, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 537600, + .voltage = 6, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 614400, + .voltage = 7, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 691200, + .voltage = 8, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 768000, + .voltage = 9, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 844800, + .voltage = 10, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 921600, + .voltage = 11, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 998400, + .voltage = 12, + }, + }, +}; + +static void +gm20b_clk_fini(struct nvkm_clk *base) +{ + struct nvkm_device *device = base->subdev.device; + struct gm20b_clk *clk = gm20b_clk(base); + + /* slide to VCO min */ + if (gk20a_pllg_is_enabled(&clk->base)) { + struct gk20a_pll pll; + u32 n_lo; + + gk20a_pllg_read_mnp(&clk->base, &pll); + n_lo = gk20a_pllg_n_lo(&clk->base, &pll); + gm20b_pllg_slide(clk, n_lo); + } + + gm20b_pllg_disable(clk); + + /* set IDDQ */ + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 1); +} + +static int +gm20b_clk_init_dvfs(struct gm20b_clk *clk) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + struct nvkm_device *device = subdev->device; + bool fused = clk->uvdet_offs && clk->uvdet_slope; + static const s32 ADC_SLOPE_UV = 10000; /* default ADC detection slope */ + u32 data; + int ret; + + /* Enable NA DVFS */ + nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_BIT, + GPCPLL_DVFS1_EN_DFS_BIT); + + /* Set VCO_CTRL */ + if (clk->dvfs_params->vco_ctrl) + nvkm_mask(device, GPCPLL_CFG3, GPCPLL_CFG3_VCO_CTRL_MASK, + clk->dvfs_params->vco_ctrl << GPCPLL_CFG3_VCO_CTRL_SHIFT); + + if (fused) { + /* Start internal calibration, but ignore results */ + nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_CAL_BIT, + GPCPLL_DVFS1_EN_DFS_CAL_BIT); + + /* got uvdev parameters from fuse, skip calibration */ + goto calibrated; + } + + /* + * If calibration parameters are not fused, start internal calibration, + * wait for completion, and use results along with default slope to + * calculate ADC offset during boot. + */ + nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_CAL_BIT, + GPCPLL_DVFS1_EN_DFS_CAL_BIT); + + /* Wait for internal calibration done (spec < 2us). */ + ret = nvkm_wait_usec(device, 10, GPCPLL_DVFS1, + GPCPLL_DVFS1_DFS_CAL_DONE_BIT, + GPCPLL_DVFS1_DFS_CAL_DONE_BIT); + if (ret < 0) { + nvkm_error(subdev, "GPCPLL calibration timeout\n"); + return -ETIMEDOUT; + } + + data = nvkm_rd32(device, GPCPLL_CFG3) >> + GPCPLL_CFG3_PLL_DFS_TESTOUT_SHIFT; + data &= MASK(GPCPLL_CFG3_PLL_DFS_TESTOUT_WIDTH); + + clk->uvdet_slope = ADC_SLOPE_UV; + clk->uvdet_offs = ((s32)clk->uv) - data * ADC_SLOPE_UV; + + nvkm_debug(subdev, "calibrated DVFS parameters: offs %d, slope %d\n", + clk->uvdet_offs, clk->uvdet_slope); + +calibrated: + /* Compute and apply initial DVFS parameters */ + gm20b_dvfs_calc_det_coeff(clk, clk->uv, &clk->dvfs); + gm20b_dvfs_program_coeff(clk, 0); + gm20b_dvfs_program_ext_cal(clk, clk->dvfs.dfs_ext_cal); + gm20b_dvfs_program_coeff(clk, clk->dvfs.dfs_coeff); + gm20b_dvfs_program_dfs_detection(clk, &clk->new_dvfs); + + return 0; +} + +/* Forward declaration to detect speedo >=1 in gm20b_clk_init() */ +static const struct nvkm_clk_func gm20b_clk; + +static int +gm20b_clk_init(struct nvkm_clk *base) +{ + struct gk20a_clk *clk = gk20a_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + int ret; + u32 data; + + /* get out from IDDQ */ + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 0); + nvkm_rd32(device, GPCPLL_CFG); + udelay(5); + + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, + GPC2CLK_OUT_INIT_VAL); + + /* Set the global bypass control to VCO */ + nvkm_mask(device, BYPASSCTRL_SYS, + MASK(BYPASSCTRL_SYS_GPCPLL_WIDTH) << BYPASSCTRL_SYS_GPCPLL_SHIFT, + 0); + + ret = gk20a_clk_setup_slide(clk); + if (ret) + return ret; + + /* If not fused, set RAM SVOP PDP data 0x2, and enable fuse override */ + data = nvkm_rd32(device, 0x021944); + if (!(data & 0x3)) { + data |= 0x2; + nvkm_wr32(device, 0x021944, data); + + data = nvkm_rd32(device, 0x021948); + data |= 0x1; + nvkm_wr32(device, 0x021948, data); + } + + /* Disable idle slow down */ + nvkm_mask(device, 0x20160, 0x003f0000, 0x0); + + /* speedo >= 1? */ + if (clk->base.func == &gm20b_clk) { + struct gm20b_clk *_clk = gm20b_clk(base); + struct nvkm_volt *volt = device->volt; + + /* Get current voltage */ + _clk->uv = nvkm_volt_get(volt); + + /* Initialize DVFS */ + ret = gm20b_clk_init_dvfs(_clk); + if (ret) + return ret; + } + + /* Start with lowest frequency */ + base->func->calc(base, &base->func->pstates[0].base); + ret = base->func->prog(base); + if (ret) { + nvkm_error(subdev, "cannot initialize clock\n"); + return ret; + } + + return 0; +} + +static const struct nvkm_clk_func +gm20b_clk_speedo0 = { + .init = gm20b_clk_init, + .fini = gk20a_clk_fini, + .read = gk20a_clk_read, + .calc = gk20a_clk_calc, + .prog = gk20a_clk_prog, + .tidy = gk20a_clk_tidy, + .pstates = gm20b_pstates, + /* Speedo 0 only supports 12 voltages */ + .nr_pstates = ARRAY_SIZE(gm20b_pstates) - 1, + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV }, + { nv_clk_src_max }, + }, +}; + +static const struct nvkm_clk_func +gm20b_clk = { + .init = gm20b_clk_init, + .fini = gm20b_clk_fini, + .read = gk20a_clk_read, + .calc = gm20b_clk_calc, + .prog = gm20b_clk_prog, + .tidy = gk20a_clk_tidy, + .pstates = gm20b_pstates, + .nr_pstates = ARRAY_SIZE(gm20b_pstates), + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV }, + { nv_clk_src_max }, + }, +}; + +static int +gm20b_clk_new_speedo0(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct gk20a_clk *clk; + int ret; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) + return -ENOMEM; + *pclk = &clk->base; + + ret = gk20a_clk_ctor(device, type, inst, &gm20b_clk_speedo0, &gm20b_pllg_params, clk); + clk->pl_to_div = pl_to_div; + clk->div_to_pl = div_to_pl; + return ret; +} + +/* FUSE register */ +#define FUSE_RESERVED_CALIB0 0x204 +#define FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_SHIFT 0 +#define FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_WIDTH 4 +#define FUSE_RESERVED_CALIB0_INTERCEPT_INT_SHIFT 4 +#define FUSE_RESERVED_CALIB0_INTERCEPT_INT_WIDTH 10 +#define FUSE_RESERVED_CALIB0_SLOPE_FRAC_SHIFT 14 +#define FUSE_RESERVED_CALIB0_SLOPE_FRAC_WIDTH 10 +#define FUSE_RESERVED_CALIB0_SLOPE_INT_SHIFT 24 +#define FUSE_RESERVED_CALIB0_SLOPE_INT_WIDTH 6 +#define FUSE_RESERVED_CALIB0_FUSE_REV_SHIFT 30 +#define FUSE_RESERVED_CALIB0_FUSE_REV_WIDTH 2 + +static int +gm20b_clk_init_fused_params(struct gm20b_clk *clk) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + u32 val = 0; + u32 rev = 0; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA) + tegra_fuse_readl(FUSE_RESERVED_CALIB0, &val); + rev = (val >> FUSE_RESERVED_CALIB0_FUSE_REV_SHIFT) & + MASK(FUSE_RESERVED_CALIB0_FUSE_REV_WIDTH); +#endif + + /* No fused parameters, we will calibrate later */ + if (rev == 0) + return -EINVAL; + + /* Integer part in mV + fractional part in uV */ + clk->uvdet_slope = ((val >> FUSE_RESERVED_CALIB0_SLOPE_INT_SHIFT) & + MASK(FUSE_RESERVED_CALIB0_SLOPE_INT_WIDTH)) * 1000 + + ((val >> FUSE_RESERVED_CALIB0_SLOPE_FRAC_SHIFT) & + MASK(FUSE_RESERVED_CALIB0_SLOPE_FRAC_WIDTH)); + + /* Integer part in mV + fractional part in 100uV */ + clk->uvdet_offs = ((val >> FUSE_RESERVED_CALIB0_INTERCEPT_INT_SHIFT) & + MASK(FUSE_RESERVED_CALIB0_INTERCEPT_INT_WIDTH)) * 1000 + + ((val >> FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_SHIFT) & + MASK(FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_WIDTH)) * 100; + + nvkm_debug(subdev, "fused calibration data: slope %d, offs %d\n", + clk->uvdet_slope, clk->uvdet_offs); + return 0; +} + +static int +gm20b_clk_init_safe_fmax(struct gm20b_clk *clk) +{ + struct nvkm_subdev *subdev = &clk->base.base.subdev; + struct nvkm_volt *volt = subdev->device->volt; + struct nvkm_pstate *pstates = clk->base.base.func->pstates; + int nr_pstates = clk->base.base.func->nr_pstates; + int vmin, id = 0; + u32 fmax = 0; + int i; + + /* find lowest voltage we can use */ + vmin = volt->vid[0].uv; + for (i = 1; i < volt->vid_nr; i++) { + if (volt->vid[i].uv <= vmin) { + vmin = volt->vid[i].uv; + id = volt->vid[i].vid; + } + } + + /* find max frequency at this voltage */ + for (i = 0; i < nr_pstates; i++) + if (pstates[i].base.voltage == id) + fmax = max(fmax, + pstates[i].base.domain[nv_clk_src_gpc]); + + if (!fmax) { + nvkm_error(subdev, "failed to evaluate safe fmax\n"); + return -EINVAL; + } + + /* we are safe at 90% of the max frequency */ + clk->safe_fmax_vmin = fmax * (100 - 10) / 100; + nvkm_debug(subdev, "safe fmax @ vmin = %u Khz\n", clk->safe_fmax_vmin); + + return 0; +} + +int +gm20b_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct nvkm_device_tegra *tdev = device->func->tegra(device); + struct gm20b_clk *clk; + struct nvkm_subdev *subdev; + struct gk20a_clk_pllg_params *clk_params; + int ret; + + /* Speedo 0 GPUs cannot use noise-aware PLL */ + if (tdev->gpu_speedo_id == 0) + return gm20b_clk_new_speedo0(device, type, inst, pclk); + + /* Speedo >= 1, use NAPLL */ + clk = kzalloc(sizeof(*clk) + sizeof(*clk_params), GFP_KERNEL); + if (!clk) + return -ENOMEM; + *pclk = &clk->base.base; + subdev = &clk->base.base.subdev; + + /* duplicate the clock parameters since we will patch them below */ + clk_params = (void *) (clk + 1); + *clk_params = gm20b_pllg_params; + ret = gk20a_clk_ctor(device, type, inst, &gm20b_clk, clk_params, &clk->base); + if (ret) + return ret; + + /* + * NAPLL can only work with max_u, clamp the m range so + * gk20a_pllg_calc_mnp always uses it + */ + clk_params->max_m = clk_params->min_m = DIV_ROUND_UP(clk_params->max_u, + (clk->base.parent_rate / KHZ)); + if (clk_params->max_m == 0) { + nvkm_warn(subdev, "cannot use NAPLL, using legacy clock...\n"); + kfree(clk); + return gm20b_clk_new_speedo0(device, type, inst, pclk); + } + + clk->base.pl_to_div = pl_to_div; + clk->base.div_to_pl = div_to_pl; + + clk->dvfs_params = &gm20b_dvfs_params; + + ret = gm20b_clk_init_fused_params(clk); + /* + * we will calibrate during init - should never happen on + * prod parts + */ + if (ret) + nvkm_warn(subdev, "no fused calibration parameters\n"); + + ret = gm20b_clk_init_safe_fmax(clk); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c new file mode 100644 index 000000000..b5f396972 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c @@ -0,0 +1,550 @@ +/* + * 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 + * Roy Spliet + */ +#define gt215_clk(p) container_of((p), struct gt215_clk, base) +#include "gt215.h" +#include "pll.h" + +#include +#include +#include +#include + +struct gt215_clk { + struct nvkm_clk base; + struct gt215_clk_info eng[nv_clk_src_max]; +}; + +static u32 read_clk(struct gt215_clk *, int, bool); +static u32 read_pll(struct gt215_clk *, int, u32); + +static u32 +read_vco(struct gt215_clk *clk, int idx) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 sctl = nvkm_rd32(device, 0x4120 + (idx * 4)); + + switch (sctl & 0x00000030) { + case 0x00000000: + return device->crystal; + case 0x00000020: + return read_pll(clk, 0x41, 0x00e820); + case 0x00000030: + return read_pll(clk, 0x42, 0x00e8a0); + default: + return 0; + } +} + +static u32 +read_clk(struct gt215_clk *clk, int idx, bool ignore_en) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 sctl, sdiv, sclk; + + /* refclk for the 0xe8xx plls is a fixed frequency */ + if (idx >= 0x40) { + if (device->chipset == 0xaf) { + /* no joke.. seriously.. sigh.. */ + return nvkm_rd32(device, 0x00471c) * 1000; + } + + return device->crystal; + } + + sctl = nvkm_rd32(device, 0x4120 + (idx * 4)); + if (!ignore_en && !(sctl & 0x00000100)) + return 0; + + /* out_alt */ + if (sctl & 0x00000400) + return 108000; + + /* vco_out */ + switch (sctl & 0x00003000) { + case 0x00000000: + if (!(sctl & 0x00000200)) + return device->crystal; + return 0; + case 0x00002000: + if (sctl & 0x00000040) + return 108000; + return 100000; + case 0x00003000: + /* vco_enable */ + if (!(sctl & 0x00000001)) + return 0; + + sclk = read_vco(clk, idx); + sdiv = ((sctl & 0x003f0000) >> 16) + 2; + return (sclk * 2) / sdiv; + default: + return 0; + } +} + +static u32 +read_pll(struct gt215_clk *clk, int idx, u32 pll) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ctrl = nvkm_rd32(device, pll + 0); + u32 sclk = 0, P = 1, N = 1, M = 1; + u32 MP; + + if (!(ctrl & 0x00000008)) { + if (ctrl & 0x00000001) { + u32 coef = nvkm_rd32(device, pll + 4); + M = (coef & 0x000000ff) >> 0; + N = (coef & 0x0000ff00) >> 8; + P = (coef & 0x003f0000) >> 16; + + /* no post-divider on these.. + * XXX: it looks more like two post-"dividers" that + * cross each other out in the default RPLL config */ + if ((pll & 0x00ff00) == 0x00e800) + P = 1; + + sclk = read_clk(clk, 0x00 + idx, false); + } + } else { + sclk = read_clk(clk, 0x10 + idx, false); + } + + MP = M * P; + + if (!MP) + return 0; + + return sclk * N / MP; +} + +static int +gt215_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct gt215_clk *clk = gt215_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 hsrc; + + switch (src) { + case nv_clk_src_crystal: + return device->crystal; + case nv_clk_src_core: + case nv_clk_src_core_intm: + return read_pll(clk, 0x00, 0x4200); + case nv_clk_src_shader: + return read_pll(clk, 0x01, 0x4220); + case nv_clk_src_mem: + return read_pll(clk, 0x02, 0x4000); + case nv_clk_src_disp: + return read_clk(clk, 0x20, false); + case nv_clk_src_vdec: + return read_clk(clk, 0x21, false); + case nv_clk_src_pmu: + return read_clk(clk, 0x25, false); + case nv_clk_src_host: + hsrc = (nvkm_rd32(device, 0xc040) & 0x30000000) >> 28; + switch (hsrc) { + case 0: + return read_clk(clk, 0x1d, false); + case 2: + case 3: + return 277000; + default: + nvkm_error(subdev, "unknown HOST clock source %d\n", hsrc); + return -EINVAL; + } + default: + nvkm_error(subdev, "invalid clock source %d\n", src); + return -EINVAL; + } + + return 0; +} + +static int +gt215_clk_info(struct nvkm_clk *base, int idx, u32 khz, + struct gt215_clk_info *info) +{ + struct gt215_clk *clk = gt215_clk(base); + u32 oclk, sclk, sdiv; + s32 diff; + + info->clk = 0; + + switch (khz) { + case 27000: + info->clk = 0x00000100; + return khz; + case 100000: + info->clk = 0x00002100; + return khz; + case 108000: + info->clk = 0x00002140; + return khz; + default: + sclk = read_vco(clk, idx); + sdiv = min((sclk * 2) / khz, (u32)65); + oclk = (sclk * 2) / sdiv; + diff = ((khz + 3000) - oclk); + + /* When imprecise, play it safe and aim for a clock lower than + * desired rather than higher */ + if (diff < 0) { + sdiv++; + oclk = (sclk * 2) / sdiv; + } + + /* divider can go as low as 2, limited here because NVIDIA + * and the VBIOS on my NVA8 seem to prefer using the PLL + * for 810MHz - is there a good reason? + * XXX: PLLs with refclk 810MHz? */ + if (sdiv > 4) { + info->clk = (((sdiv - 2) << 16) | 0x00003100); + return oclk; + } + + break; + } + + return -ERANGE; +} + +int +gt215_pll_info(struct nvkm_clk *base, int idx, u32 pll, u32 khz, + struct gt215_clk_info *info) +{ + struct gt215_clk *clk = gt215_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvbios_pll limits; + int P, N, M, diff; + int ret; + + info->pll = 0; + + /* If we can get a within [-2, 3) MHz of a divider, we'll disable the + * PLL and use the divider instead. */ + ret = gt215_clk_info(&clk->base, idx, khz, info); + diff = khz - ret; + if (!pll || (diff >= -2000 && diff < 3000)) { + goto out; + } + + /* Try with PLL */ + ret = nvbios_pll_parse(subdev->device->bios, pll, &limits); + if (ret) + return ret; + + ret = gt215_clk_info(&clk->base, idx - 0x10, limits.refclk, info); + if (ret != limits.refclk) + return -EINVAL; + + ret = gt215_pll_calc(subdev, &limits, khz, &N, NULL, &M, &P); + if (ret >= 0) { + info->pll = (P << 16) | (N << 8) | M; + } + +out: + info->fb_delay = max(((khz + 7566) / 15133), (u32) 18); + return ret ? ret : -ERANGE; +} + +static int +calc_clk(struct gt215_clk *clk, struct nvkm_cstate *cstate, + int idx, u32 pll, int dom) +{ + int ret = gt215_pll_info(&clk->base, idx, pll, cstate->domain[dom], + &clk->eng[dom]); + if (ret >= 0) + return 0; + return ret; +} + +static int +calc_host(struct gt215_clk *clk, struct nvkm_cstate *cstate) +{ + int ret = 0; + u32 kHz = cstate->domain[nv_clk_src_host]; + struct gt215_clk_info *info = &clk->eng[nv_clk_src_host]; + + if (kHz == 277000) { + info->clk = 0; + info->host_out = NVA3_HOST_277; + return 0; + } + + info->host_out = NVA3_HOST_CLK; + + ret = gt215_clk_info(&clk->base, 0x1d, kHz, info); + if (ret >= 0) + return 0; + + return ret; +} + +int +gt215_clk_pre(struct nvkm_clk *clk, unsigned long *flags) +{ + struct nvkm_device *device = clk->subdev.device; + struct nvkm_fifo *fifo = device->fifo; + + /* halt and idle execution engines */ + nvkm_mask(device, 0x020060, 0x00070000, 0x00000000); + nvkm_mask(device, 0x002504, 0x00000001, 0x00000001); + /* Wait until the interrupt handler is finished */ + if (nvkm_msec(device, 2000, + if (!nvkm_rd32(device, 0x000100)) + break; + ) < 0) + return -EBUSY; + + if (fifo) + nvkm_fifo_pause(fifo, flags); + + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x002504) & 0x00000010) + break; + ) < 0) + return -EIO; + + if (nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x00251c) & 0x0000003f; + if (tmp == 0x0000003f) + break; + ) < 0) + return -EIO; + + return 0; +} + +void +gt215_clk_post(struct nvkm_clk *clk, unsigned long *flags) +{ + struct nvkm_device *device = clk->subdev.device; + struct nvkm_fifo *fifo = device->fifo; + + if (fifo && flags) + nvkm_fifo_start(fifo, flags); + + nvkm_mask(device, 0x002504, 0x00000001, 0x00000000); + nvkm_mask(device, 0x020060, 0x00070000, 0x00040000); +} + +static void +disable_clk_src(struct gt215_clk *clk, u32 src) +{ + struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, src, 0x00000100, 0x00000000); + nvkm_mask(device, src, 0x00000001, 0x00000000); +} + +static void +prog_pll(struct gt215_clk *clk, int idx, u32 pll, int dom) +{ + struct gt215_clk_info *info = &clk->eng[dom]; + struct nvkm_device *device = clk->base.subdev.device; + const u32 src0 = 0x004120 + (idx * 4); + const u32 src1 = 0x004160 + (idx * 4); + const u32 ctrl = pll + 0; + const u32 coef = pll + 4; + u32 bypass; + + if (info->pll) { + /* Always start from a non-PLL clock */ + bypass = nvkm_rd32(device, ctrl) & 0x00000008; + if (!bypass) { + nvkm_mask(device, src1, 0x00000101, 0x00000101); + nvkm_mask(device, ctrl, 0x00000008, 0x00000008); + udelay(20); + } + + nvkm_mask(device, src0, 0x003f3141, 0x00000101 | info->clk); + nvkm_wr32(device, coef, info->pll); + nvkm_mask(device, ctrl, 0x00000015, 0x00000015); + nvkm_mask(device, ctrl, 0x00000010, 0x00000000); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, ctrl) & 0x00020000) + break; + ) < 0) { + nvkm_mask(device, ctrl, 0x00000010, 0x00000010); + nvkm_mask(device, src0, 0x00000101, 0x00000000); + return; + } + nvkm_mask(device, ctrl, 0x00000010, 0x00000010); + nvkm_mask(device, ctrl, 0x00000008, 0x00000000); + disable_clk_src(clk, src1); + } else { + nvkm_mask(device, src1, 0x003f3141, 0x00000101 | info->clk); + nvkm_mask(device, ctrl, 0x00000018, 0x00000018); + udelay(20); + nvkm_mask(device, ctrl, 0x00000001, 0x00000000); + disable_clk_src(clk, src0); + } +} + +static void +prog_clk(struct gt215_clk *clk, int idx, int dom) +{ + struct gt215_clk_info *info = &clk->eng[dom]; + struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, 0x004120 + (idx * 4), 0x003f3141, 0x00000101 | info->clk); +} + +static void +prog_host(struct gt215_clk *clk) +{ + struct gt215_clk_info *info = &clk->eng[nv_clk_src_host]; + struct nvkm_device *device = clk->base.subdev.device; + u32 hsrc = (nvkm_rd32(device, 0xc040)); + + switch (info->host_out) { + case NVA3_HOST_277: + if ((hsrc & 0x30000000) == 0) { + nvkm_wr32(device, 0xc040, hsrc | 0x20000000); + disable_clk_src(clk, 0x4194); + } + break; + case NVA3_HOST_CLK: + prog_clk(clk, 0x1d, nv_clk_src_host); + if ((hsrc & 0x30000000) >= 0x20000000) { + nvkm_wr32(device, 0xc040, hsrc & ~0x30000000); + } + break; + default: + break; + } + + /* This seems to be a clock gating factor on idle, always set to 64 */ + nvkm_wr32(device, 0xc044, 0x3e); +} + +static void +prog_core(struct gt215_clk *clk, int dom) +{ + struct gt215_clk_info *info = &clk->eng[dom]; + struct nvkm_device *device = clk->base.subdev.device; + u32 fb_delay = nvkm_rd32(device, 0x10002c); + + if (fb_delay < info->fb_delay) + nvkm_wr32(device, 0x10002c, info->fb_delay); + + prog_pll(clk, 0x00, 0x004200, dom); + + if (fb_delay > info->fb_delay) + nvkm_wr32(device, 0x10002c, info->fb_delay); +} + +static int +gt215_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct gt215_clk *clk = gt215_clk(base); + struct gt215_clk_info *core = &clk->eng[nv_clk_src_core]; + int ret; + + if ((ret = calc_clk(clk, cstate, 0x10, 0x4200, nv_clk_src_core)) || + (ret = calc_clk(clk, cstate, 0x11, 0x4220, nv_clk_src_shader)) || + (ret = calc_clk(clk, cstate, 0x20, 0x0000, nv_clk_src_disp)) || + (ret = calc_clk(clk, cstate, 0x21, 0x0000, nv_clk_src_vdec)) || + (ret = calc_host(clk, cstate))) + return ret; + + /* XXX: Should be reading the highest bit in the VBIOS clock to decide + * whether to use a PLL or not... but using a PLL defeats the purpose */ + if (core->pll) { + ret = gt215_clk_info(&clk->base, 0x10, + cstate->domain[nv_clk_src_core_intm], + &clk->eng[nv_clk_src_core_intm]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int +gt215_clk_prog(struct nvkm_clk *base) +{ + struct gt215_clk *clk = gt215_clk(base); + struct gt215_clk_info *core = &clk->eng[nv_clk_src_core]; + int ret = 0; + unsigned long flags; + unsigned long *f = &flags; + + ret = gt215_clk_pre(&clk->base, f); + if (ret) + goto out; + + if (core->pll) + prog_core(clk, nv_clk_src_core_intm); + + prog_core(clk, nv_clk_src_core); + prog_pll(clk, 0x01, 0x004220, nv_clk_src_shader); + prog_clk(clk, 0x20, nv_clk_src_disp); + prog_clk(clk, 0x21, nv_clk_src_vdec); + prog_host(clk); + +out: + if (ret == -EBUSY) + f = NULL; + + gt215_clk_post(&clk->base, f); + return ret; +} + +static void +gt215_clk_tidy(struct nvkm_clk *base) +{ +} + +static const struct nvkm_clk_func +gt215_clk = { + .read = gt215_clk_read, + .calc = gt215_clk_calc, + .prog = gt215_clk_prog, + .tidy = gt215_clk_tidy, + .domains = { + { nv_clk_src_crystal , 0xff }, + { nv_clk_src_core , 0x00, 0, "core", 1000 }, + { nv_clk_src_shader , 0x01, 0, "shader", 1000 }, + { nv_clk_src_mem , 0x02, 0, "memory", 1000 }, + { nv_clk_src_vdec , 0x03 }, + { nv_clk_src_disp , 0x04 }, + { nv_clk_src_host , 0x05 }, + { nv_clk_src_core_intm, 0x06 }, + { nv_clk_src_max } + } +}; + +int +gt215_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct gt215_clk *clk; + + if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) + return -ENOMEM; + *pclk = &clk->base; + + return nvkm_clk_ctor(>215_clk, device, type, inst, true, &clk->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h new file mode 100644 index 000000000..34754efbf --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_CLK_NVA3_H__ +#define __NVKM_CLK_NVA3_H__ +#include "priv.h" + +struct gt215_clk_info { + u32 clk; + u32 pll; + enum { + NVA3_HOST_277, + NVA3_HOST_CLK, + } host_out; + u32 fb_delay; +}; + +int gt215_pll_info(struct nvkm_clk *, int, u32, u32, struct gt215_clk_info *); +int gt215_clk_pre(struct nvkm_clk *, unsigned long *flags); +void gt215_clk_post(struct nvkm_clk *, unsigned long *flags); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c new file mode 100644 index 000000000..81f103f88 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c @@ -0,0 +1,422 @@ +/* + * 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 + */ +#define mcp77_clk(p) container_of((p), struct mcp77_clk, base) +#include "gt215.h" +#include "pll.h" + +#include +#include +#include + +struct mcp77_clk { + struct nvkm_clk base; + enum nv_clk_src csrc, ssrc, vsrc; + u32 cctrl, sctrl; + u32 ccoef, scoef; + u32 cpost, spost; + u32 vdiv; +}; + +static u32 +read_div(struct mcp77_clk *clk) +{ + struct nvkm_device *device = clk->base.subdev.device; + return nvkm_rd32(device, 0x004600); +} + +static u32 +read_pll(struct mcp77_clk *clk, u32 base) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ctrl = nvkm_rd32(device, base + 0); + u32 coef = nvkm_rd32(device, base + 4); + u32 ref = nvkm_clk_read(&clk->base, nv_clk_src_href); + u32 post_div = 0; + u32 clock = 0; + int N1, M1; + + switch (base){ + case 0x4020: + post_div = 1 << ((nvkm_rd32(device, 0x4070) & 0x000f0000) >> 16); + break; + case 0x4028: + post_div = (nvkm_rd32(device, 0x4040) & 0x000f0000) >> 16; + break; + default: + break; + } + + N1 = (coef & 0x0000ff00) >> 8; + M1 = (coef & 0x000000ff); + if ((ctrl & 0x80000000) && M1) { + clock = ref * N1 / M1; + clock = clock / post_div; + } + + return clock; +} + +static int +mcp77_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct mcp77_clk *clk = mcp77_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 mast = nvkm_rd32(device, 0x00c054); + u32 P = 0; + + switch (src) { + case nv_clk_src_crystal: + return device->crystal; + case nv_clk_src_href: + return 100000; /* PCIE reference clock */ + case nv_clk_src_hclkm4: + return nvkm_clk_read(&clk->base, nv_clk_src_href) * 4; + case nv_clk_src_hclkm2d3: + return nvkm_clk_read(&clk->base, nv_clk_src_href) * 2 / 3; + case nv_clk_src_host: + switch (mast & 0x000c0000) { + case 0x00000000: return nvkm_clk_read(&clk->base, nv_clk_src_hclkm2d3); + case 0x00040000: break; + case 0x00080000: return nvkm_clk_read(&clk->base, nv_clk_src_hclkm4); + case 0x000c0000: return nvkm_clk_read(&clk->base, nv_clk_src_cclk); + } + break; + case nv_clk_src_core: + P = (nvkm_rd32(device, 0x004028) & 0x00070000) >> 16; + + switch (mast & 0x00000003) { + case 0x00000000: return nvkm_clk_read(&clk->base, nv_clk_src_crystal) >> P; + case 0x00000001: return 0; + case 0x00000002: return nvkm_clk_read(&clk->base, nv_clk_src_hclkm4) >> P; + case 0x00000003: return read_pll(clk, 0x004028) >> P; + } + break; + case nv_clk_src_cclk: + if ((mast & 0x03000000) != 0x03000000) + return nvkm_clk_read(&clk->base, nv_clk_src_core); + + if ((mast & 0x00000200) == 0x00000000) + return nvkm_clk_read(&clk->base, nv_clk_src_core); + + switch (mast & 0x00000c00) { + case 0x00000000: return nvkm_clk_read(&clk->base, nv_clk_src_href); + case 0x00000400: return nvkm_clk_read(&clk->base, nv_clk_src_hclkm4); + case 0x00000800: return nvkm_clk_read(&clk->base, nv_clk_src_hclkm2d3); + default: return 0; + } + case nv_clk_src_shader: + P = (nvkm_rd32(device, 0x004020) & 0x00070000) >> 16; + switch (mast & 0x00000030) { + case 0x00000000: + if (mast & 0x00000040) + return nvkm_clk_read(&clk->base, nv_clk_src_href) >> P; + return nvkm_clk_read(&clk->base, nv_clk_src_crystal) >> P; + case 0x00000010: break; + case 0x00000020: return read_pll(clk, 0x004028) >> P; + case 0x00000030: return read_pll(clk, 0x004020) >> P; + } + break; + case nv_clk_src_mem: + return 0; + case nv_clk_src_vdec: + P = (read_div(clk) & 0x00000700) >> 8; + + switch (mast & 0x00400000) { + case 0x00400000: + return nvkm_clk_read(&clk->base, nv_clk_src_core) >> P; + default: + return 500000 >> P; + } + break; + default: + break; + } + + nvkm_debug(subdev, "unknown clock source %d %08x\n", src, mast); + return 0; +} + +static u32 +calc_pll(struct mcp77_clk *clk, u32 reg, + u32 clock, int *N, int *M, int *P) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvbios_pll pll; + int ret; + + ret = nvbios_pll_parse(subdev->device->bios, reg, &pll); + if (ret) + return 0; + + pll.vco2.max_freq = 0; + pll.refclk = nvkm_clk_read(&clk->base, nv_clk_src_href); + if (!pll.refclk) + return 0; + + return nv04_pll_calc(subdev, &pll, clock, N, M, NULL, NULL, P); +} + +static inline u32 +calc_P(u32 src, u32 target, int *div) +{ + u32 clk0 = src, clk1 = src; + for (*div = 0; *div <= 7; (*div)++) { + if (clk0 <= target) { + clk1 = clk0 << (*div ? 1 : 0); + break; + } + clk0 >>= 1; + } + + if (target - clk0 <= clk1 - target) + return clk0; + (*div)--; + return clk1; +} + +static int +mcp77_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct mcp77_clk *clk = mcp77_clk(base); + const int shader = cstate->domain[nv_clk_src_shader]; + const int core = cstate->domain[nv_clk_src_core]; + const int vdec = cstate->domain[nv_clk_src_vdec]; + struct nvkm_subdev *subdev = &clk->base.subdev; + u32 out = 0, clock = 0; + int N, M, P1, P2 = 0; + int divs = 0; + + /* cclk: find suitable source, disable PLL if we can */ + if (core < nvkm_clk_read(&clk->base, nv_clk_src_hclkm4)) + out = calc_P(nvkm_clk_read(&clk->base, nv_clk_src_hclkm4), core, &divs); + + /* Calculate clock * 2, so shader clock can use it too */ + clock = calc_pll(clk, 0x4028, (core << 1), &N, &M, &P1); + + if (abs(core - out) <= abs(core - (clock >> 1))) { + clk->csrc = nv_clk_src_hclkm4; + clk->cctrl = divs << 16; + } else { + /* NVCTRL is actually used _after_ NVPOST, and after what we + * call NVPLL. To make matters worse, NVPOST is an integer + * divider instead of a right-shift number. */ + if(P1 > 2) { + P2 = P1 - 2; + P1 = 2; + } + + clk->csrc = nv_clk_src_core; + clk->ccoef = (N << 8) | M; + + clk->cctrl = (P2 + 1) << 16; + clk->cpost = (1 << P1) << 16; + } + + /* sclk: nvpll + divisor, href or spll */ + out = 0; + if (shader == nvkm_clk_read(&clk->base, nv_clk_src_href)) { + clk->ssrc = nv_clk_src_href; + } else { + clock = calc_pll(clk, 0x4020, shader, &N, &M, &P1); + if (clk->csrc == nv_clk_src_core) + out = calc_P((core << 1), shader, &divs); + + if (abs(shader - out) <= + abs(shader - clock) && + (divs + P2) <= 7) { + clk->ssrc = nv_clk_src_core; + clk->sctrl = (divs + P2) << 16; + } else { + clk->ssrc = nv_clk_src_shader; + clk->scoef = (N << 8) | M; + clk->sctrl = P1 << 16; + } + } + + /* vclk */ + out = calc_P(core, vdec, &divs); + clock = calc_P(500000, vdec, &P1); + if(abs(vdec - out) <= abs(vdec - clock)) { + clk->vsrc = nv_clk_src_cclk; + clk->vdiv = divs << 16; + } else { + clk->vsrc = nv_clk_src_vdec; + clk->vdiv = P1 << 16; + } + + /* Print strategy! */ + nvkm_debug(subdev, "nvpll: %08x %08x %08x\n", + clk->ccoef, clk->cpost, clk->cctrl); + nvkm_debug(subdev, " spll: %08x %08x %08x\n", + clk->scoef, clk->spost, clk->sctrl); + nvkm_debug(subdev, " vdiv: %08x\n", clk->vdiv); + if (clk->csrc == nv_clk_src_hclkm4) + nvkm_debug(subdev, "core: hrefm4\n"); + else + nvkm_debug(subdev, "core: nvpll\n"); + + if (clk->ssrc == nv_clk_src_hclkm4) + nvkm_debug(subdev, "shader: hrefm4\n"); + else if (clk->ssrc == nv_clk_src_core) + nvkm_debug(subdev, "shader: nvpll\n"); + else + nvkm_debug(subdev, "shader: spll\n"); + + if (clk->vsrc == nv_clk_src_hclkm4) + nvkm_debug(subdev, "vdec: 500MHz\n"); + else + nvkm_debug(subdev, "vdec: core\n"); + + return 0; +} + +static int +mcp77_clk_prog(struct nvkm_clk *base) +{ + struct mcp77_clk *clk = mcp77_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 pllmask = 0, mast; + unsigned long flags; + unsigned long *f = &flags; + int ret = 0; + + ret = gt215_clk_pre(&clk->base, f); + if (ret) + goto out; + + /* First switch to safe clocks: href */ + mast = nvkm_mask(device, 0xc054, 0x03400e70, 0x03400640); + mast &= ~0x00400e73; + mast |= 0x03000000; + + switch (clk->csrc) { + case nv_clk_src_hclkm4: + nvkm_mask(device, 0x4028, 0x00070000, clk->cctrl); + mast |= 0x00000002; + break; + case nv_clk_src_core: + nvkm_wr32(device, 0x402c, clk->ccoef); + nvkm_wr32(device, 0x4028, 0x80000000 | clk->cctrl); + nvkm_wr32(device, 0x4040, clk->cpost); + pllmask |= (0x3 << 8); + mast |= 0x00000003; + break; + default: + nvkm_warn(subdev, "Reclocking failed: unknown core clock\n"); + goto resume; + } + + switch (clk->ssrc) { + case nv_clk_src_href: + nvkm_mask(device, 0x4020, 0x00070000, 0x00000000); + /* mast |= 0x00000000; */ + break; + case nv_clk_src_core: + nvkm_mask(device, 0x4020, 0x00070000, clk->sctrl); + mast |= 0x00000020; + break; + case nv_clk_src_shader: + nvkm_wr32(device, 0x4024, clk->scoef); + nvkm_wr32(device, 0x4020, 0x80000000 | clk->sctrl); + nvkm_wr32(device, 0x4070, clk->spost); + pllmask |= (0x3 << 12); + mast |= 0x00000030; + break; + default: + nvkm_warn(subdev, "Reclocking failed: unknown sclk clock\n"); + goto resume; + } + + if (nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x004080) & pllmask; + if (tmp == pllmask) + break; + ) < 0) + goto resume; + + switch (clk->vsrc) { + case nv_clk_src_cclk: + mast |= 0x00400000; + fallthrough; + default: + nvkm_wr32(device, 0x4600, clk->vdiv); + } + + nvkm_wr32(device, 0xc054, mast); + +resume: + /* Disable some PLLs and dividers when unused */ + if (clk->csrc != nv_clk_src_core) { + nvkm_wr32(device, 0x4040, 0x00000000); + nvkm_mask(device, 0x4028, 0x80000000, 0x00000000); + } + + if (clk->ssrc != nv_clk_src_shader) { + nvkm_wr32(device, 0x4070, 0x00000000); + nvkm_mask(device, 0x4020, 0x80000000, 0x00000000); + } + +out: + if (ret == -EBUSY) + f = NULL; + + gt215_clk_post(&clk->base, f); + return ret; +} + +static void +mcp77_clk_tidy(struct nvkm_clk *base) +{ +} + +static const struct nvkm_clk_func +mcp77_clk = { + .read = mcp77_clk_read, + .calc = mcp77_clk_calc, + .prog = mcp77_clk_prog, + .tidy = mcp77_clk_tidy, + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_href , 0xff }, + { nv_clk_src_core , 0xff, 0, "core", 1000 }, + { nv_clk_src_shader , 0xff, 0, "shader", 1000 }, + { nv_clk_src_vdec , 0xff, 0, "vdec", 1000 }, + { nv_clk_src_max } + } +}; + +int +mcp77_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct mcp77_clk *clk; + + if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) + return -ENOMEM; + *pclk = &clk->base; + + return nvkm_clk_ctor(&mcp77_clk, device, type, inst, true, &clk->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c new file mode 100644 index 000000000..ca13598c2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c @@ -0,0 +1,84 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pll.h" + +#include +#include +#include + +int +nv04_clk_pll_calc(struct nvkm_clk *clock, struct nvbios_pll *info, + int clk, struct nvkm_pll_vals *pv) +{ + int N1, M1, N2, M2, P; + int ret = nv04_pll_calc(&clock->subdev, info, clk, &N1, &M1, &N2, &M2, &P); + if (ret) { + pv->refclk = info->refclk; + pv->N1 = N1; + pv->M1 = M1; + pv->N2 = N2; + pv->M2 = M2; + pv->log2P = P; + } + return ret; +} + +int +nv04_clk_pll_prog(struct nvkm_clk *clk, u32 reg1, struct nvkm_pll_vals *pv) +{ + struct nvkm_device *device = clk->subdev.device; + struct nvkm_devinit *devinit = device->devinit; + int cv = device->bios->version.chip; + + if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || + cv >= 0x40) { + if (reg1 > 0x405c) + setPLL_double_highregs(devinit, reg1, pv); + else + setPLL_double_lowregs(devinit, reg1, pv); + } else + setPLL_single(devinit, reg1, pv); + + return 0; +} + +static const struct nvkm_clk_func +nv04_clk = { + .domains = { + { nv_clk_src_max } + } +}; + +int +nv04_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + int ret = nvkm_clk_new_(&nv04_clk, device, type, inst, false, pclk); + if (ret == 0) { + (*pclk)->pll_calc = nv04_clk_pll_calc; + (*pclk)->pll_prog = nv04_clk_pll_prog; + } + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c new file mode 100644 index 000000000..7ddd8cecb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c @@ -0,0 +1,233 @@ +/* + * 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 + */ +#define nv40_clk(p) container_of((p), struct nv40_clk, base) +#include "priv.h" +#include "pll.h" + +#include +#include + +struct nv40_clk { + struct nvkm_clk base; + u32 ctrl; + u32 npll_ctrl; + u32 npll_coef; + u32 spll; +}; + +static u32 +read_pll_1(struct nv40_clk *clk, u32 reg) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ctrl = nvkm_rd32(device, reg + 0x00); + int P = (ctrl & 0x00070000) >> 16; + int N = (ctrl & 0x0000ff00) >> 8; + int M = (ctrl & 0x000000ff) >> 0; + u32 ref = 27000, khz = 0; + + if (ctrl & 0x80000000) + khz = ref * N / M; + + return khz >> P; +} + +static u32 +read_pll_2(struct nv40_clk *clk, u32 reg) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 ctrl = nvkm_rd32(device, reg + 0x00); + u32 coef = nvkm_rd32(device, reg + 0x04); + int N2 = (coef & 0xff000000) >> 24; + int M2 = (coef & 0x00ff0000) >> 16; + int N1 = (coef & 0x0000ff00) >> 8; + int M1 = (coef & 0x000000ff) >> 0; + int P = (ctrl & 0x00070000) >> 16; + u32 ref = 27000, khz = 0; + + if ((ctrl & 0x80000000) && M1) { + khz = ref * N1 / M1; + if ((ctrl & 0x40000100) == 0x40000000) { + if (M2) + khz = khz * N2 / M2; + else + khz = 0; + } + } + + return khz >> P; +} + +static u32 +read_clk(struct nv40_clk *clk, u32 src) +{ + switch (src) { + case 3: + return read_pll_2(clk, 0x004000); + case 2: + return read_pll_1(clk, 0x004008); + default: + break; + } + + return 0; +} + +static int +nv40_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct nv40_clk *clk = nv40_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 mast = nvkm_rd32(device, 0x00c040); + + switch (src) { + case nv_clk_src_crystal: + return device->crystal; + case nv_clk_src_href: + return 100000; /*XXX: PCIE/AGP differ*/ + case nv_clk_src_core: + return read_clk(clk, (mast & 0x00000003) >> 0); + case nv_clk_src_shader: + return read_clk(clk, (mast & 0x00000030) >> 4); + case nv_clk_src_mem: + return read_pll_2(clk, 0x4020); + default: + break; + } + + nvkm_debug(subdev, "unknown clock source %d %08x\n", src, mast); + return -EINVAL; +} + +static int +nv40_clk_calc_pll(struct nv40_clk *clk, u32 reg, u32 khz, + int *N1, int *M1, int *N2, int *M2, int *log2P) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvbios_pll pll; + int ret; + + ret = nvbios_pll_parse(subdev->device->bios, reg, &pll); + if (ret) + return ret; + + if (khz < pll.vco1.max_freq) + pll.vco2.max_freq = 0; + + ret = nv04_pll_calc(subdev, &pll, khz, N1, M1, N2, M2, log2P); + if (ret == 0) + return -ERANGE; + + return ret; +} + +static int +nv40_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct nv40_clk *clk = nv40_clk(base); + int gclk = cstate->domain[nv_clk_src_core]; + int sclk = cstate->domain[nv_clk_src_shader]; + int N1, M1, N2, M2, log2P; + int ret; + + /* core/geometric clock */ + ret = nv40_clk_calc_pll(clk, 0x004000, gclk, + &N1, &M1, &N2, &M2, &log2P); + if (ret < 0) + return ret; + + if (N2 == M2) { + clk->npll_ctrl = 0x80000100 | (log2P << 16); + clk->npll_coef = (N1 << 8) | M1; + } else { + clk->npll_ctrl = 0xc0000000 | (log2P << 16); + clk->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1; + } + + /* use the second pll for shader/rop clock, if it differs from core */ + if (sclk && sclk != gclk) { + ret = nv40_clk_calc_pll(clk, 0x004008, sclk, + &N1, &M1, NULL, NULL, &log2P); + if (ret < 0) + return ret; + + clk->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1; + clk->ctrl = 0x00000223; + } else { + clk->spll = 0x00000000; + clk->ctrl = 0x00000333; + } + + return 0; +} + +static int +nv40_clk_prog(struct nvkm_clk *base) +{ + struct nv40_clk *clk = nv40_clk(base); + struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, 0x00c040, 0x00000333, 0x00000000); + nvkm_wr32(device, 0x004004, clk->npll_coef); + nvkm_mask(device, 0x004000, 0xc0070100, clk->npll_ctrl); + nvkm_mask(device, 0x004008, 0xc007ffff, clk->spll); + mdelay(5); + nvkm_mask(device, 0x00c040, 0x00000333, clk->ctrl); + return 0; +} + +static void +nv40_clk_tidy(struct nvkm_clk *obj) +{ +} + +static const struct nvkm_clk_func +nv40_clk = { + .read = nv40_clk_read, + .calc = nv40_clk_calc, + .prog = nv40_clk_prog, + .tidy = nv40_clk_tidy, + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_href , 0xff }, + { nv_clk_src_core , 0xff, 0, "core", 1000 }, + { nv_clk_src_shader , 0xff, 0, "shader", 1000 }, + { nv_clk_src_mem , 0xff, 0, "memory", 1000 }, + { nv_clk_src_max } + } +}; + +int +nv40_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct nv40_clk *clk; + + if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) + return -ENOMEM; + clk->base.pll_calc = nv04_clk_pll_calc; + clk->base.pll_prog = nv04_clk_pll_prog; + *pclk = &clk->base; + + return nvkm_clk_ctor(&nv40_clk, device, type, inst, true, &clk->base); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c new file mode 100644 index 000000000..e1d31c62f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c @@ -0,0 +1,563 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "pll.h" +#include "seq.h" + +#include +#include + +static u32 +read_div(struct nv50_clk *clk) +{ + struct nvkm_device *device = clk->base.subdev.device; + switch (device->chipset) { + case 0x50: /* it exists, but only has bit 31, not the dividers.. */ + case 0x84: + case 0x86: + case 0x98: + case 0xa0: + return nvkm_rd32(device, 0x004700); + case 0x92: + case 0x94: + case 0x96: + return nvkm_rd32(device, 0x004800); + default: + return 0x00000000; + } +} + +static u32 +read_pll_src(struct nv50_clk *clk, u32 base) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 coef, ref = nvkm_clk_read(&clk->base, nv_clk_src_crystal); + u32 rsel = nvkm_rd32(device, 0x00e18c); + int P, N, M, id; + + switch (device->chipset) { + case 0x50: + case 0xa0: + switch (base) { + case 0x4020: + case 0x4028: id = !!(rsel & 0x00000004); break; + case 0x4008: id = !!(rsel & 0x00000008); break; + case 0x4030: id = 0; break; + default: + nvkm_error(subdev, "ref: bad pll %06x\n", base); + return 0; + } + + coef = nvkm_rd32(device, 0x00e81c + (id * 0x0c)); + ref *= (coef & 0x01000000) ? 2 : 4; + P = (coef & 0x00070000) >> 16; + N = ((coef & 0x0000ff00) >> 8) + 1; + M = ((coef & 0x000000ff) >> 0) + 1; + break; + case 0x84: + case 0x86: + case 0x92: + coef = nvkm_rd32(device, 0x00e81c); + P = (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + case 0x94: + case 0x96: + case 0x98: + rsel = nvkm_rd32(device, 0x00c050); + switch (base) { + case 0x4020: rsel = (rsel & 0x00000003) >> 0; break; + case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break; + case 0x4028: rsel = (rsel & 0x00001800) >> 11; break; + case 0x4030: rsel = 3; break; + default: + nvkm_error(subdev, "ref: bad pll %06x\n", base); + return 0; + } + + switch (rsel) { + case 0: id = 1; break; + case 1: return nvkm_clk_read(&clk->base, nv_clk_src_crystal); + case 2: return nvkm_clk_read(&clk->base, nv_clk_src_href); + case 3: id = 0; break; + } + + coef = nvkm_rd32(device, 0x00e81c + (id * 0x28)); + P = (nvkm_rd32(device, 0x00e824 + (id * 0x28)) >> 16) & 7; + P += (coef & 0x00070000) >> 16; + N = (coef & 0x0000ff00) >> 8; + M = (coef & 0x000000ff) >> 0; + break; + default: + BUG(); + } + + if (M) + return (ref * N / M) >> P; + + return 0; +} + +static u32 +read_pll_ref(struct nv50_clk *clk, u32 base) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 src, mast = nvkm_rd32(device, 0x00c040); + + switch (base) { + case 0x004028: + src = !!(mast & 0x00200000); + break; + case 0x004020: + src = !!(mast & 0x00400000); + break; + case 0x004008: + src = !!(mast & 0x00010000); + break; + case 0x004030: + src = !!(mast & 0x02000000); + break; + case 0x00e810: + return nvkm_clk_read(&clk->base, nv_clk_src_crystal); + default: + nvkm_error(subdev, "bad pll %06x\n", base); + return 0; + } + + if (src) + return nvkm_clk_read(&clk->base, nv_clk_src_href); + + return read_pll_src(clk, base); +} + +static u32 +read_pll(struct nv50_clk *clk, u32 base) +{ + struct nvkm_device *device = clk->base.subdev.device; + u32 mast = nvkm_rd32(device, 0x00c040); + u32 ctrl = nvkm_rd32(device, base + 0); + u32 coef = nvkm_rd32(device, base + 4); + u32 ref = read_pll_ref(clk, base); + u32 freq = 0; + int N1, N2, M1, M2; + + if (base == 0x004028 && (mast & 0x00100000)) { + /* wtf, appears to only disable post-divider on gt200 */ + if (device->chipset != 0xa0) + return nvkm_clk_read(&clk->base, nv_clk_src_dom6); + } + + N2 = (coef & 0xff000000) >> 24; + M2 = (coef & 0x00ff0000) >> 16; + N1 = (coef & 0x0000ff00) >> 8; + M1 = (coef & 0x000000ff); + if ((ctrl & 0x80000000) && M1) { + freq = ref * N1 / M1; + if ((ctrl & 0x40000100) == 0x40000000) { + if (M2) + freq = freq * N2 / M2; + else + freq = 0; + } + } + + return freq; +} + +int +nv50_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct nv50_clk *clk = nv50_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + u32 mast = nvkm_rd32(device, 0x00c040); + u32 P = 0; + + switch (src) { + case nv_clk_src_crystal: + return device->crystal; + case nv_clk_src_href: + return 100000; /* PCIE reference clock */ + case nv_clk_src_hclk: + return div_u64((u64)nvkm_clk_read(&clk->base, nv_clk_src_href) * 27778, 10000); + case nv_clk_src_hclkm3: + return nvkm_clk_read(&clk->base, nv_clk_src_hclk) * 3; + case nv_clk_src_hclkm3d2: + return nvkm_clk_read(&clk->base, nv_clk_src_hclk) * 3 / 2; + case nv_clk_src_host: + switch (mast & 0x30000000) { + case 0x00000000: return nvkm_clk_read(&clk->base, nv_clk_src_href); + case 0x10000000: break; + case 0x20000000: /* !0x50 */ + case 0x30000000: return nvkm_clk_read(&clk->base, nv_clk_src_hclk); + } + break; + case nv_clk_src_core: + if (!(mast & 0x00100000)) + P = (nvkm_rd32(device, 0x004028) & 0x00070000) >> 16; + switch (mast & 0x00000003) { + case 0x00000000: return nvkm_clk_read(&clk->base, nv_clk_src_crystal) >> P; + case 0x00000001: return nvkm_clk_read(&clk->base, nv_clk_src_dom6); + case 0x00000002: return read_pll(clk, 0x004020) >> P; + case 0x00000003: return read_pll(clk, 0x004028) >> P; + } + break; + case nv_clk_src_shader: + P = (nvkm_rd32(device, 0x004020) & 0x00070000) >> 16; + switch (mast & 0x00000030) { + case 0x00000000: + if (mast & 0x00000080) + return nvkm_clk_read(&clk->base, nv_clk_src_host) >> P; + return nvkm_clk_read(&clk->base, nv_clk_src_crystal) >> P; + case 0x00000010: break; + case 0x00000020: return read_pll(clk, 0x004028) >> P; + case 0x00000030: return read_pll(clk, 0x004020) >> P; + } + break; + case nv_clk_src_mem: + P = (nvkm_rd32(device, 0x004008) & 0x00070000) >> 16; + if (nvkm_rd32(device, 0x004008) & 0x00000200) { + switch (mast & 0x0000c000) { + case 0x00000000: + return nvkm_clk_read(&clk->base, nv_clk_src_crystal) >> P; + case 0x00008000: + case 0x0000c000: + return nvkm_clk_read(&clk->base, nv_clk_src_href) >> P; + } + } else { + return read_pll(clk, 0x004008) >> P; + } + break; + case nv_clk_src_vdec: + P = (read_div(clk) & 0x00000700) >> 8; + switch (device->chipset) { + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0xa0: + switch (mast & 0x00000c00) { + case 0x00000000: + if (device->chipset == 0xa0) /* wtf?? */ + return nvkm_clk_read(&clk->base, nv_clk_src_core) >> P; + return nvkm_clk_read(&clk->base, nv_clk_src_crystal) >> P; + case 0x00000400: + return 0; + case 0x00000800: + if (mast & 0x01000000) + return read_pll(clk, 0x004028) >> P; + return read_pll(clk, 0x004030) >> P; + case 0x00000c00: + return nvkm_clk_read(&clk->base, nv_clk_src_core) >> P; + } + break; + case 0x98: + switch (mast & 0x00000c00) { + case 0x00000000: + return nvkm_clk_read(&clk->base, nv_clk_src_core) >> P; + case 0x00000400: + return 0; + case 0x00000800: + return nvkm_clk_read(&clk->base, nv_clk_src_hclkm3d2) >> P; + case 0x00000c00: + return nvkm_clk_read(&clk->base, nv_clk_src_mem) >> P; + } + break; + } + break; + case nv_clk_src_dom6: + switch (device->chipset) { + case 0x50: + case 0xa0: + return read_pll(clk, 0x00e810) >> 2; + case 0x84: + case 0x86: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + P = (read_div(clk) & 0x00000007) >> 0; + switch (mast & 0x0c000000) { + case 0x00000000: return nvkm_clk_read(&clk->base, nv_clk_src_href); + case 0x04000000: break; + case 0x08000000: return nvkm_clk_read(&clk->base, nv_clk_src_hclk); + case 0x0c000000: + return nvkm_clk_read(&clk->base, nv_clk_src_hclkm3) >> P; + } + break; + default: + break; + } + break; + default: + break; + } + + nvkm_debug(subdev, "unknown clock source %d %08x\n", src, mast); + return -EINVAL; +} + +static u32 +calc_pll(struct nv50_clk *clk, u32 reg, u32 idx, int *N, int *M, int *P) +{ + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvbios_pll pll; + int ret; + + ret = nvbios_pll_parse(subdev->device->bios, reg, &pll); + if (ret) + return 0; + + pll.vco2.max_freq = 0; + pll.refclk = read_pll_ref(clk, reg); + if (!pll.refclk) + return 0; + + return nv04_pll_calc(subdev, &pll, idx, N, M, NULL, NULL, P); +} + +static inline u32 +calc_div(u32 src, u32 target, int *div) +{ + u32 clk0 = src, clk1 = src; + for (*div = 0; *div <= 7; (*div)++) { + if (clk0 <= target) { + clk1 = clk0 << (*div ? 1 : 0); + break; + } + clk0 >>= 1; + } + + if (target - clk0 <= clk1 - target) + return clk0; + (*div)--; + return clk1; +} + +static inline u32 +clk_same(u32 a, u32 b) +{ + return ((a / 1000) == (b / 1000)); +} + +int +nv50_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct nv50_clk *clk = nv50_clk(base); + struct nv50_clk_hwsq *hwsq = &clk->hwsq; + struct nvkm_subdev *subdev = &clk->base.subdev; + struct nvkm_device *device = subdev->device; + const int shader = cstate->domain[nv_clk_src_shader]; + const int core = cstate->domain[nv_clk_src_core]; + const int vdec = cstate->domain[nv_clk_src_vdec]; + const int dom6 = cstate->domain[nv_clk_src_dom6]; + u32 mastm = 0, mastv = 0; + u32 divsm = 0, divsv = 0; + int N, M, P1, P2; + int freq, out; + + /* prepare a hwsq script from which we'll perform the reclock */ + out = clk_init(hwsq, subdev); + if (out) + return out; + + clk_wr32(hwsq, fifo, 0x00000001); /* block fifo */ + clk_nsec(hwsq, 8000); + clk_setf(hwsq, 0x10, 0x00); /* disable fb */ + clk_wait(hwsq, 0x00, 0x01); /* wait for fb disabled */ + + /* vdec: avoid modifying xpll until we know exactly how the other + * clock domains work, i suspect at least some of them can also be + * tied to xpll... + */ + if (vdec) { + /* see how close we can get using nvclk as a source */ + freq = calc_div(core, vdec, &P1); + + /* see how close we can get using xpll/hclk as a source */ + if (device->chipset != 0x98) + out = read_pll(clk, 0x004030); + else + out = nvkm_clk_read(&clk->base, nv_clk_src_hclkm3d2); + out = calc_div(out, vdec, &P2); + + /* select whichever gets us closest */ + if (abs(vdec - freq) <= abs(vdec - out)) { + if (device->chipset != 0x98) + mastv |= 0x00000c00; + divsv |= P1 << 8; + } else { + mastv |= 0x00000800; + divsv |= P2 << 8; + } + + mastm |= 0x00000c00; + divsm |= 0x00000700; + } + + /* dom6: nfi what this is, but we're limited to various combinations + * of the host clock frequency + */ + if (dom6) { + if (clk_same(dom6, nvkm_clk_read(&clk->base, nv_clk_src_href))) { + mastv |= 0x00000000; + } else + if (clk_same(dom6, nvkm_clk_read(&clk->base, nv_clk_src_hclk))) { + mastv |= 0x08000000; + } else { + freq = nvkm_clk_read(&clk->base, nv_clk_src_hclk) * 3; + calc_div(freq, dom6, &P1); + + mastv |= 0x0c000000; + divsv |= P1; + } + + mastm |= 0x0c000000; + divsm |= 0x00000007; + } + + /* vdec/dom6: switch to "safe" clocks temporarily, update dividers + * and then switch to target clocks + */ + clk_mask(hwsq, mast, mastm, 0x00000000); + clk_mask(hwsq, divs, divsm, divsv); + clk_mask(hwsq, mast, mastm, mastv); + + /* core/shader: disconnect nvclk/sclk from their PLLs (nvclk to dom6, + * sclk to hclk) before reprogramming + */ + if (device->chipset < 0x92) + clk_mask(hwsq, mast, 0x001000b0, 0x00100080); + else + clk_mask(hwsq, mast, 0x000000b3, 0x00000081); + + /* core: for the moment at least, always use nvpll */ + freq = calc_pll(clk, 0x4028, core, &N, &M, &P1); + if (freq == 0) + return -ERANGE; + + clk_mask(hwsq, nvpll[0], 0xc03f0100, + 0x80000000 | (P1 << 19) | (P1 << 16)); + clk_mask(hwsq, nvpll[1], 0x0000ffff, (N << 8) | M); + + /* shader: tie to nvclk if possible, otherwise use spll. have to be + * very careful that the shader clock is at least twice the core, or + * some chipsets will be very unhappy. i expect most or all of these + * cases will be handled by tying to nvclk, but it's possible there's + * corners + */ + if (P1-- && shader == (core << 1)) { + clk_mask(hwsq, spll[0], 0xc03f0100, (P1 << 19) | (P1 << 16)); + clk_mask(hwsq, mast, 0x00100033, 0x00000023); + } else { + freq = calc_pll(clk, 0x4020, shader, &N, &M, &P1); + if (freq == 0) + return -ERANGE; + + clk_mask(hwsq, spll[0], 0xc03f0100, + 0x80000000 | (P1 << 19) | (P1 << 16)); + clk_mask(hwsq, spll[1], 0x0000ffff, (N << 8) | M); + clk_mask(hwsq, mast, 0x00100033, 0x00000033); + } + + /* restore normal operation */ + clk_setf(hwsq, 0x10, 0x01); /* enable fb */ + clk_wait(hwsq, 0x00, 0x00); /* wait for fb enabled */ + clk_wr32(hwsq, fifo, 0x00000000); /* un-block fifo */ + return 0; +} + +int +nv50_clk_prog(struct nvkm_clk *base) +{ + struct nv50_clk *clk = nv50_clk(base); + return clk_exec(&clk->hwsq, true); +} + +void +nv50_clk_tidy(struct nvkm_clk *base) +{ + struct nv50_clk *clk = nv50_clk(base); + clk_exec(&clk->hwsq, false); +} + +int +nv50_clk_new_(const struct nvkm_clk_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, bool allow_reclock, struct nvkm_clk **pclk) +{ + struct nv50_clk *clk; + int ret; + + if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) + return -ENOMEM; + ret = nvkm_clk_ctor(func, device, type, inst, allow_reclock, &clk->base); + *pclk = &clk->base; + if (ret) + return ret; + + clk->hwsq.r_fifo = hwsq_reg(0x002504); + clk->hwsq.r_spll[0] = hwsq_reg(0x004020); + clk->hwsq.r_spll[1] = hwsq_reg(0x004024); + clk->hwsq.r_nvpll[0] = hwsq_reg(0x004028); + clk->hwsq.r_nvpll[1] = hwsq_reg(0x00402c); + switch (device->chipset) { + case 0x92: + case 0x94: + case 0x96: + clk->hwsq.r_divs = hwsq_reg(0x004800); + break; + default: + clk->hwsq.r_divs = hwsq_reg(0x004700); + break; + } + clk->hwsq.r_mast = hwsq_reg(0x00c040); + return 0; +} + +static const struct nvkm_clk_func +nv50_clk = { + .read = nv50_clk_read, + .calc = nv50_clk_calc, + .prog = nv50_clk_prog, + .tidy = nv50_clk_tidy, + .domains = { + { nv_clk_src_crystal, 0xff }, + { nv_clk_src_href , 0xff }, + { nv_clk_src_core , 0xff, 0, "core", 1000 }, + { nv_clk_src_shader , 0xff, 0, "shader", 1000 }, + { nv_clk_src_mem , 0xff, 0, "memory", 1000 }, + { nv_clk_src_max } + } +}; + +int +nv50_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + return nv50_clk_new_(&nv50_clk, device, type, inst, false, pclk); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h new file mode 100644 index 000000000..5b4cb7e5c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_CLK_H__ +#define __NV50_CLK_H__ +#define nv50_clk(p) container_of((p), struct nv50_clk, base) +#include "priv.h" + +#include + +struct nv50_clk_hwsq { + struct hwsq base; + struct hwsq_reg r_fifo; + struct hwsq_reg r_spll[2]; + struct hwsq_reg r_nvpll[2]; + struct hwsq_reg r_divs; + struct hwsq_reg r_mast; +}; + +struct nv50_clk { + struct nvkm_clk base; + struct nv50_clk_hwsq hwsq; +}; + +int nv50_clk_new_(const struct nvkm_clk_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + bool, struct nvkm_clk **); +int nv50_clk_read(struct nvkm_clk *, enum nv_clk_src); +int nv50_clk_calc(struct nvkm_clk *, struct nvkm_cstate *); +int nv50_clk_prog(struct nvkm_clk *); +void nv50_clk_tidy(struct nvkm_clk *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h new file mode 100644 index 000000000..631907564 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PLL_H__ +#define __NVKM_PLL_H__ +#include +struct nvkm_subdev; +struct nvbios_pll; + +int nv04_pll_calc(struct nvkm_subdev *, struct nvbios_pll *, u32 freq, + int *N1, int *M1, int *N2, int *M2, int *P); +int gt215_pll_calc(struct nvkm_subdev *, struct nvbios_pll *, u32 freq, + int *N, int *fN, int *M, int *P); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c new file mode 100644 index 000000000..c6fccd600 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c @@ -0,0 +1,87 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "pll.h" + +#include +#include + +int +gt215_pll_calc(struct nvkm_subdev *subdev, struct nvbios_pll *info, + u32 freq, int *pN, int *pfN, int *pM, int *P) +{ + u32 best_err = ~0, err; + int M, lM, hM, N, fN; + + *P = info->vco1.max_freq / freq; + if (*P > info->max_p) + *P = info->max_p; + if (*P < info->min_p) + *P = info->min_p; + + lM = (info->refclk + info->vco1.max_inputfreq) / info->vco1.max_inputfreq; + 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; + N = tmp / info->refclk; + fN = tmp % info->refclk; + + if (!pfN) { + if (fN >= info->refclk / 2) + N++; + } else { + if (fN < info->refclk / 2) + N--; + fN = tmp - (N * info->refclk); + } + + if (N < info->vco1.min_n) + continue; + if (N > info->vco1.max_n) + break; + + err = abs(freq - (info->refclk * N / M / *P)); + if (err < best_err) { + best_err = err; + *pN = N; + *pM = M; + } + + if (pfN) { + *pfN = ((fN << 13) + info->refclk / 2) / info->refclk; + *pfN = (*pfN - 4096) & 0xffff; + return freq; + } + } + + if (unlikely(best_err == ~0)) { + nvkm_error(subdev, "unable to find matching pll values\n"); + return -EINVAL; + } + + return info->refclk * *pN / *pM / *P; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c new file mode 100644 index 000000000..5ad67879e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c @@ -0,0 +1,245 @@ +/* + * Copyright 1993-2003 NVIDIA, Corporation + * Copyright 2007-2009 Stuart Bennett + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "pll.h" + +#include +#include + +static int +getMNP_single(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk, + int *pN, int *pM, int *pP) +{ + /* Find M, N and P for a single stage PLL + * + * Note that some bioses (NV3x) have lookup tables of precomputed MNP + * values, but we're too lazy to use those atm + * + * "clk" parameter in kHz + * returns calculated clock + */ + struct nvkm_bios *bios = subdev->device->bios; + int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq; + int minM = info->vco1.min_m, maxM = info->vco1.max_m; + int minN = info->vco1.min_n, maxN = info->vco1.max_n; + int minU = info->vco1.min_inputfreq; + int maxU = info->vco1.max_inputfreq; + int minP = info->min_p; + int maxP = info->max_p_usable; + int crystal = info->refclk; + int M, N, thisP, P; + int clkP, calcclk; + int delta, bestdelta = INT_MAX; + int bestclk = 0; + + /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */ + /* possibly correlated with introduction of 27MHz crystal */ + if (bios->version.major < 0x60) { + int cv = bios->version.chip; + if (cv < 0x17 || cv == 0x1a || cv == 0x20) { + if (clk > 250000) + maxM = 6; + if (clk > 340000) + maxM = 2; + } else if (cv < 0x40) { + if (clk > 150000) + maxM = 6; + if (clk > 200000) + maxM = 4; + if (clk > 340000) + maxM = 2; + } + } + + P = 1 << maxP; + if ((clk * P) < minvco) { + minvco = clk * maxP; + maxvco = minvco * 2; + } + + if (clk + clk/200 > maxvco) /* +0.5% */ + maxvco = clk + clk/200; + + /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */ + for (thisP = minP; thisP <= maxP; thisP++) { + P = 1 << thisP; + clkP = clk * P; + + if (clkP < minvco) + continue; + if (clkP > maxvco) + return bestclk; + + for (M = minM; M <= maxM; M++) { + if (crystal/M < minU) + return bestclk; + if (crystal/M > maxU) + continue; + + /* add crystal/2 to round better */ + N = (clkP * M + crystal/2) / crystal; + + if (N < minN) + continue; + if (N > maxN) + break; + + /* more rounding additions */ + calcclk = ((N * crystal + P/2) / P + M/2) / M; + delta = abs(calcclk - clk); + /* we do an exhaustive search rather than terminating + * on an optimality condition... + */ + if (delta < bestdelta) { + bestdelta = delta; + bestclk = calcclk; + *pN = N; + *pM = M; + *pP = thisP; + if (delta == 0) /* except this one */ + return bestclk; + } + } + } + + return bestclk; +} + +static int +getMNP_double(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk, + int *pN1, int *pM1, int *pN2, int *pM2, int *pP) +{ + /* Find M, N and P for a two stage PLL + * + * Note that some bioses (NV30+) have lookup tables of precomputed MNP + * values, but we're too lazy to use those atm + * + * "clk" parameter in kHz + * returns calculated clock + */ + int chip_version = subdev->device->bios->version.chip; + int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq; + int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq; + int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq; + int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq; + int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m; + int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n; + int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m; + int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n; + int maxlog2P = info->max_p_usable; + int crystal = info->refclk; + bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2); + int M1, N1, M2, N2, log2P; + int clkP, calcclk1, calcclk2, calcclkout; + int delta, bestdelta = INT_MAX; + int bestclk = 0; + + int vco2 = (maxvco2 - maxvco2/200) / 2; + for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++) + ; + clkP = clk << log2P; + + if (maxvco2 < clk + clk/200) /* +0.5% */ + maxvco2 = clk + clk/200; + + for (M1 = minM1; M1 <= maxM1; M1++) { + if (crystal/M1 < minU1) + return bestclk; + if (crystal/M1 > maxU1) + continue; + + for (N1 = minN1; N1 <= maxN1; N1++) { + calcclk1 = crystal * N1 / M1; + if (calcclk1 < minvco1) + continue; + if (calcclk1 > maxvco1) + break; + + for (M2 = minM2; M2 <= maxM2; M2++) { + if (calcclk1/M2 < minU2) + break; + if (calcclk1/M2 > maxU2) + continue; + + /* add calcclk1/2 to round better */ + N2 = (clkP * M2 + calcclk1/2) / calcclk1; + if (N2 < minN2) + continue; + if (N2 > maxN2) + break; + + if (!fixedgain2) { + if (chip_version < 0x60) + if (N2/M2 < 4 || N2/M2 > 10) + continue; + + calcclk2 = calcclk1 * N2 / M2; + if (calcclk2 < minvco2) + break; + if (calcclk2 > maxvco2) + continue; + } else + calcclk2 = calcclk1; + + calcclkout = calcclk2 >> log2P; + delta = abs(calcclkout - clk); + /* we do an exhaustive search rather than terminating + * on an optimality condition... + */ + if (delta < bestdelta) { + bestdelta = delta; + bestclk = calcclkout; + *pN1 = N1; + *pM1 = M1; + *pN2 = N2; + *pM2 = M2; + *pP = log2P; + if (delta == 0) /* except this one */ + return bestclk; + } + } + } + } + + return bestclk; +} + +int +nv04_pll_calc(struct nvkm_subdev *subdev, struct nvbios_pll *info, u32 freq, + int *N1, int *M1, int *N2, int *M2, int *P) +{ + int ret; + + if (!info->vco2.max_freq || !N2) { + ret = getMNP_single(subdev, info, freq, N1, M1, P); + if (N2) { + *N2 = 1; + *M2 = 1; + } + } else { + ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P); + } + + if (!ret) + nvkm_error(subdev, "unable to compute acceptable pll values\n"); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/priv.h new file mode 100644 index 000000000..810cc572c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/priv.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_CLK_PRIV_H__ +#define __NVKM_CLK_PRIV_H__ +#define nvkm_clk(p) container_of((p), struct nvkm_clk, subdev) +#include + +struct nvkm_clk_func { + int (*init)(struct nvkm_clk *); + void (*fini)(struct nvkm_clk *); + int (*read)(struct nvkm_clk *, enum nv_clk_src); + int (*calc)(struct nvkm_clk *, struct nvkm_cstate *); + int (*prog)(struct nvkm_clk *); + void (*tidy)(struct nvkm_clk *); + struct nvkm_pstate *pstates; + int nr_pstates; + struct nvkm_domain domains[]; +}; + +int nvkm_clk_ctor(const struct nvkm_clk_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + bool allow_reclock, struct nvkm_clk *); +int nvkm_clk_new_(const struct nvkm_clk_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + bool allow_reclock, struct nvkm_clk **); + +int nv04_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, int clk, + struct nvkm_pll_vals *); +int nv04_clk_pll_prog(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h new file mode 100644 index 000000000..e4b362d34 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_CLK_SEQ_H__ +#define __NVKM_CLK_SEQ_H__ +#include + +#define clk_init(s,p) hwsq_init(&(s)->base, (p)) +#define clk_exec(s,e) hwsq_exec(&(s)->base, (e)) +#define clk_have(s,r) ((s)->r_##r.addr != 0x000000) +#define clk_rd32(s,r) hwsq_rd32(&(s)->base, &(s)->r_##r) +#define clk_wr32(s,r,d) hwsq_wr32(&(s)->base, &(s)->r_##r, (d)) +#define clk_mask(s,r,m,d) hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define clk_setf(s,f,d) hwsq_setf(&(s)->base, (f), (d)) +#define clk_wait(s,f,d) hwsq_wait(&(s)->base, (f), (d)) +#define clk_nsec(s,n) hwsq_nsec(&(s)->base, (n)) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild new file mode 100644 index 000000000..d1abb6484 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/devinit/base.o +nvkm-y += nvkm/subdev/devinit/nv04.o +nvkm-y += nvkm/subdev/devinit/nv05.o +nvkm-y += nvkm/subdev/devinit/nv10.o +nvkm-y += nvkm/subdev/devinit/nv1a.o +nvkm-y += nvkm/subdev/devinit/nv20.o +nvkm-y += nvkm/subdev/devinit/nv50.o +nvkm-y += nvkm/subdev/devinit/g84.o +nvkm-y += nvkm/subdev/devinit/g98.o +nvkm-y += nvkm/subdev/devinit/gt215.o +nvkm-y += nvkm/subdev/devinit/mcp89.o +nvkm-y += nvkm/subdev/devinit/gf100.o +nvkm-y += nvkm/subdev/devinit/gm107.o +nvkm-y += nvkm/subdev/devinit/gm200.o +nvkm-y += nvkm/subdev/devinit/gv100.o +nvkm-y += nvkm/subdev/devinit/tu102.o +nvkm-y += nvkm/subdev/devinit/ga100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c new file mode 100644 index 000000000..dd4981708 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c @@ -0,0 +1,135 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +u32 +nvkm_devinit_mmio(struct nvkm_devinit *init, u32 addr) +{ + if (init->func->mmio) + addr = init->func->mmio(init, addr); + return addr; +} + +int +nvkm_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 khz) +{ + return init->func->pll_set(init, type, khz); +} + +void +nvkm_devinit_meminit(struct nvkm_devinit *init) +{ + if (init->func->meminit) + init->func->meminit(init); +} + +u64 +nvkm_devinit_disable(struct nvkm_devinit *init) +{ + if (init && init->func->disable) + return init->func->disable(init); + return 0; +} + +int +nvkm_devinit_post(struct nvkm_devinit *init) +{ + int ret = 0; + if (init && init->func->post) + ret = init->func->post(init, init->post); + nvkm_devinit_disable(init); + return ret; +} + +static int +nvkm_devinit_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + /* force full reinit on resume */ + if (suspend) + init->post = true; + return 0; +} + +static int +nvkm_devinit_preinit(struct nvkm_subdev *subdev) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + + if (init->func->preinit) + init->func->preinit(init); + + /* Override the post flag during the first call if NvForcePost is set */ + if (init->force_post) { + init->post = init->force_post; + init->force_post = false; + } + + /* unlock the extended vga crtc regs */ + nvkm_lockvgac(subdev->device, false); + return 0; +} + +static int +nvkm_devinit_init(struct nvkm_subdev *subdev) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + if (init->func->init) + init->func->init(init); + return 0; +} + +static void * +nvkm_devinit_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_devinit *init = nvkm_devinit(subdev); + void *data = init; + + if (init->func->dtor) + data = init->func->dtor(init); + + /* lock crtc regs */ + nvkm_lockvgac(subdev->device, true); + return data; +} + +static const struct nvkm_subdev_func +nvkm_devinit = { + .dtor = nvkm_devinit_dtor, + .preinit = nvkm_devinit_preinit, + .init = nvkm_devinit_init, + .fini = nvkm_devinit_fini, +}; + +void +nvkm_devinit_ctor(const struct nvkm_devinit_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_devinit *init) +{ + nvkm_subdev_ctor(&nvkm_devinit, device, type, inst, &init->subdev); + init->func = func; + init->force_post = nvkm_boolopt(device->cfgopt, "NvForcePost", false); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h new file mode 100644 index 000000000..6c5bbff12 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include + +#define NV04_PFB_DEBUG_0 0x00100080 +# define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001 +# define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010 +# define NV04_PFB_DEBUG_0_REFRESH_COUNTX64 0x00003f00 +# define NV04_PFB_DEBUG_0_REFRESH_SLOW_CLK 0x00004000 +# define NV04_PFB_DEBUG_0_SAFE_MODE 0x00008000 +# define NV04_PFB_DEBUG_0_ALOM_ENABLE 0x00010000 +# define NV04_PFB_DEBUG_0_CASOE 0x00100000 +# define NV04_PFB_DEBUG_0_CKE_INVERT 0x10000000 +# define NV04_PFB_DEBUG_0_REFINC 0x20000000 +# define NV04_PFB_DEBUG_0_SAVE_POWER_OFF 0x40000000 +#define NV04_PFB_CFG0 0x00100200 +# define NV04_PFB_CFG0_SCRAMBLE 0x20000000 +#define NV04_PFB_CFG1 0x00100204 +#define NV04_PFB_SCRAMBLE(i) (0x00100400 + 4 * (i)) + +#define NV10_PFB_REFCTRL 0x00100210 +# define NV10_PFB_REFCTRL_VALID_1 (1 << 31) + +static inline struct io_mapping * +fbmem_init(struct nvkm_device *dev) +{ + return io_mapping_create_wc(dev->func->resource_addr(dev, 1), + dev->func->resource_size(dev, 1)); +} + +static inline void +fbmem_fini(struct io_mapping *fb) +{ + io_mapping_free(fb); +} + +static inline u32 +fbmem_peek(struct io_mapping *fb, u32 off) +{ + u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK); + u32 val = ioread32(p + (off & ~PAGE_MASK)); + io_mapping_unmap_atomic(p); + return val; +} + +static inline void +fbmem_poke(struct io_mapping *fb, u32 off, u32 val) +{ + u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK); + iowrite32(val, p + (off & ~PAGE_MASK)); + wmb(); + io_mapping_unmap_atomic(p); +} + +static inline bool +fbmem_readback(struct io_mapping *fb, u32 off, u32 val) +{ + fbmem_poke(fb, off, val); + return val == fbmem_peek(fb, off); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c new file mode 100644 index 000000000..c224702b7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include + +static u64 +g84_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MPEG, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_VP, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_BSP, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_CIPHER, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_BSP, 0); + if (!(r00154c & 0x00000040)) + nvkm_subdev_disable(device, NVKM_ENGINE_CIPHER, 0); + + return disable; +} + +static const struct nvkm_devinit_func +g84_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = nv50_devinit_pll_set, + .disable = g84_devinit_disable, +}; + +int +g84_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&g84_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c new file mode 100644 index 000000000..8977483a9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c @@ -0,0 +1,66 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include + +static u64 +g98_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (!(r00154c & 0x00000040)) + nvkm_subdev_disable(device, NVKM_ENGINE_SEC, 0); + + return 0ULL; +} + +static const struct nvkm_devinit_func +g98_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = nv50_devinit_pll_set, + .disable = g98_devinit_disable, +}; + +int +g98_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&g98_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c new file mode 100644 index 000000000..6b280b05c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c @@ -0,0 +1,77 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nv50.h" + +#include +#include +#include + +static int +ga100_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int head = type - PLL_VPLL0; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_wr32(device, 0x00ef00 + (head * 0x40), 0x02080004); + nvkm_wr32(device, 0x00ef18 + (head * 0x40), (N << 16) | fN); + nvkm_wr32(device, 0x00ef04 + (head * 0x40), (P << 16) | M); + nvkm_wr32(device, 0x00e9c0 + (head * 0x04), 0x00000001); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct nvkm_devinit_func +ga100_devinit = { + .init = nv50_devinit_init, + .post = tu102_devinit_post, + .pll_set = ga100_devinit_pll_set, +}; + +int +ga100_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&ga100_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c new file mode 100644 index 000000000..5b7cb1fe7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c @@ -0,0 +1,120 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include + +int +gf100_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_mask(device, info.reg + 0x0c, 0x00000000, 0x00000100); + nvkm_wr32(device, info.reg + 0x04, (P << 16) | (N << 8) | M); + nvkm_wr32(device, info.reg + 0x10, fN << 16); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static u64 +gf100_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r022500 = nvkm_rd32(device, 0x022500); + + if (r022500 & 0x00000001) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + + if (r022500 & 0x00000002) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (r022500 & 0x00000004) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (r022500 & 0x00000008) + nvkm_subdev_disable(device, NVKM_ENGINE_MSENC, 0); + if (r022500 & 0x00000100) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + if (r022500 & 0x00000200) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 1); + + return 0ULL; +} + +void +gf100_devinit_preinit(struct nvkm_devinit *base) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + + /* + * This bit is set by devinit, and flips back to 0 on suspend. We + * can use it as a reliable way to know whether we should run devinit. + */ + base->post = ((nvkm_rd32(device, 0x2240c) & BIT(1)) == 0); +} + +static const struct nvkm_devinit_func +gf100_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = gf100_devinit_pll_set, + .disable = gf100_devinit_disable, +}; + +int +gf100_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gf100_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c new file mode 100644 index 000000000..8955af270 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c @@ -0,0 +1,60 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include + +u64 +gm107_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r021c00 = nvkm_rd32(device, 0x021c00); + u32 r021c04 = nvkm_rd32(device, 0x021c04); + + if (r021c00 & 0x00000001) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + if (r021c00 & 0x00000004) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 2); + if (r021c04 & 0x00000001) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + + return 0ULL; +} + +static const struct nvkm_devinit_func +gm107_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = gf100_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +gm107_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gm107_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c new file mode 100644 index 000000000..a308b9bde --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c @@ -0,0 +1,186 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include + +static void +pmu_code(struct nv50_devinit *init, u32 pmu, u32 img, u32 len, bool sec) +{ + struct nvkm_device *device = init->base.subdev.device; + struct nvkm_bios *bios = device->bios; + int i; + + nvkm_wr32(device, 0x10a180, 0x01000000 | (sec ? 0x10000000 : 0) | pmu); + for (i = 0; i < len; i += 4) { + if ((i & 0xff) == 0) + nvkm_wr32(device, 0x10a188, (pmu + i) >> 8); + nvkm_wr32(device, 0x10a184, nvbios_rd32(bios, img + i)); + } + + while (i & 0xff) { + nvkm_wr32(device, 0x10a184, 0x00000000); + i += 4; + } +} + +static void +pmu_data(struct nv50_devinit *init, u32 pmu, u32 img, u32 len) +{ + struct nvkm_device *device = init->base.subdev.device; + struct nvkm_bios *bios = device->bios; + int i; + + nvkm_wr32(device, 0x10a1c0, 0x01000000 | pmu); + for (i = 0; i < len; i += 4) + nvkm_wr32(device, 0x10a1c4, nvbios_rd32(bios, img + i)); +} + +static u32 +pmu_args(struct nv50_devinit *init, u32 argp, u32 argi) +{ + struct nvkm_device *device = init->base.subdev.device; + nvkm_wr32(device, 0x10a1c0, argp); + nvkm_wr32(device, 0x10a1c0, nvkm_rd32(device, 0x10a1c4) + argi); + return nvkm_rd32(device, 0x10a1c4); +} + +static void +pmu_exec(struct nv50_devinit *init, u32 init_addr) +{ + struct nvkm_device *device = init->base.subdev.device; + nvkm_wr32(device, 0x10a104, init_addr); + nvkm_wr32(device, 0x10a10c, 0x00000000); + nvkm_wr32(device, 0x10a100, 0x00000002); +} + +static int +pmu_load(struct nv50_devinit *init, u8 type, bool post, + u32 *init_addr_pmu, u32 *args_addr_pmu) +{ + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_pmuR pmu; + + if (!nvbios_pmuRm(bios, type, &pmu)) + return -EINVAL; + + if (!post) + return 0; + + pmu_code(init, pmu.boot_addr_pmu, pmu.boot_addr, pmu.boot_size, false); + pmu_code(init, pmu.code_addr_pmu, pmu.code_addr, pmu.code_size, true); + pmu_data(init, pmu.data_addr_pmu, pmu.data_addr, pmu.data_size); + + if (init_addr_pmu) { + *init_addr_pmu = pmu.init_addr_pmu; + *args_addr_pmu = pmu.args_addr_pmu; + return 0; + } + + return pmu_exec(init, pmu.init_addr_pmu), 0; +} + +void +gm200_devinit_preos(struct nv50_devinit *init, bool post) +{ + /* Optional: Execute PRE_OS application on PMU, which should at + * least take care of fans until a full PMU has been loaded. + */ + pmu_load(init, 0x01, post, NULL, NULL); +} + +int +gm200_devinit_post(struct nvkm_devinit *base, bool post) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct bit_entry bit_I; + u32 exec, args; + int ret; + + if (bit_entry(bios, 'I', &bit_I) || bit_I.version != 1 || + bit_I.length < 0x1c) { + nvkm_error(subdev, "VBIOS PMU init data not found\n"); + return -EINVAL; + } + + /* Upload DEVINIT application from VBIOS onto PMU. */ + ret = pmu_load(init, 0x04, post, &exec, &args); + if (ret) { + nvkm_error(subdev, "VBIOS PMU/DEVINIT not found\n"); + return ret; + } + + /* Upload tables required by opcodes in boot scripts. */ + if (post) { + u32 pmu = pmu_args(init, args + 0x08, 0x08); + u32 img = nvbios_rd16(bios, bit_I.offset + 0x14); + u32 len = nvbios_rd16(bios, bit_I.offset + 0x16); + pmu_data(init, pmu, img, len); + } + + /* Upload boot scripts. */ + if (post) { + u32 pmu = pmu_args(init, args + 0x08, 0x10); + u32 img = nvbios_rd16(bios, bit_I.offset + 0x18); + u32 len = nvbios_rd16(bios, bit_I.offset + 0x1a); + pmu_data(init, pmu, img, len); + } + + /* Execute DEVINIT. */ + if (post) { + nvkm_wr32(device, 0x10a040, 0x00005000); + pmu_exec(init, exec); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x10a040) & 0x00002000) + break; + ) < 0) + return -ETIMEDOUT; + } + + gm200_devinit_preos(init, post); + return 0; +} + +static const struct nvkm_devinit_func +gm200_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = gm200_devinit_post, + .pll_set = gf100_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +gm200_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gm200_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c new file mode 100644 index 000000000..3d0ab86c3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c @@ -0,0 +1,152 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include + +int +gt215_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nvkm_wr32(device, info.reg + 0, 0x50000610); + nvkm_mask(device, info.reg + 4, 0x003fffff, + (P << 16) | (M << 8) | N); + nvkm_wr32(device, info.reg + 8, fN); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static u64 +gt215_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (!(r00154c & 0x00000200)) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + + return 0ULL; +} + +static u32 +gt215_devinit_mmio_part[] = { + 0x100720, 0x1008bc, 4, + 0x100a20, 0x100adc, 4, + 0x100d80, 0x100ddc, 4, + 0x110000, 0x110f9c, 4, + 0x111000, 0x11103c, 8, + 0x111080, 0x1110fc, 4, + 0x111120, 0x1111fc, 4, + 0x111300, 0x1114bc, 4, + 0, +}; + +static u32 +gt215_devinit_mmio(struct nvkm_devinit *base, u32 addr) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_device *device = init->base.subdev.device; + u32 *mmio = gt215_devinit_mmio_part; + + /* the init tables on some boards have INIT_RAM_RESTRICT_ZM_REG_GROUP + * instructions which touch registers that may not even exist on + * some configurations (Quadro 400), which causes the register + * interface to screw up for some amount of time after attempting to + * write to one of these, and results in all sorts of things going + * horribly wrong. + * + * the binary driver avoids touching these registers at all, however, + * the video bios doesn't care and does what the scripts say. it's + * presumed that the io-port access to init registers isn't effected + * by the screw-up bug mentioned above. + * + * really, a new opcode should've been invented to handle these + * requirements, but whatever, it's too late for that now. + */ + while (mmio[0]) { + if (addr >= mmio[0] && addr <= mmio[1]) { + u32 part = (addr / mmio[2]) & 7; + if (!init->r001540) + init->r001540 = nvkm_rd32(device, 0x001540); + if (part >= hweight8((init->r001540 >> 16) & 0xff)) + return ~0; + return addr; + } + mmio += 3; + } + + return addr; +} + +static const struct nvkm_devinit_func +gt215_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .mmio = gt215_devinit_mmio, + .pll_set = gt215_devinit_pll_set, + .disable = gt215_devinit_disable, +}; + +int +gt215_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(>215_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c new file mode 100644 index 000000000..b4d168851 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gv100.c @@ -0,0 +1,79 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nv50.h" + +#include +#include +#include + +static int +gv100_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int head = type - PLL_VPLL0; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_wr32(device, 0x00ef10 + (head * 0x40), fN << 16); + nvkm_wr32(device, 0x00ef04 + (head * 0x40), (P << 16) | + (N << 8) | + (M << 0)); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct nvkm_devinit_func +gv100_devinit = { + .preinit = gf100_devinit_preinit, + .init = nv50_devinit_init, + .post = gm200_devinit_post, + .pll_set = gv100_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +gv100_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&gv100_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c new file mode 100644 index 000000000..a9cdf2411 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c @@ -0,0 +1,67 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include + +static u64 +mcp89_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u32 r00154c = nvkm_rd32(device, 0x00154c); + + if (!(r001540 & 0x40000000)) { + nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0); + nvkm_subdev_disable(device, NVKM_ENGINE_MSPPP, 0); + } + + if (!(r00154c & 0x00000004)) + nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0); + if (!(r00154c & 0x00000020)) + nvkm_subdev_disable(device, NVKM_ENGINE_MSVLD, 0); + if (!(r00154c & 0x00000040)) + nvkm_subdev_disable(device, NVKM_ENGINE_VIC, 0); + if (!(r00154c & 0x00000200)) + nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0); + + return 0; +} + +static const struct nvkm_devinit_func +mcp89_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = gt215_devinit_pll_set, + .disable = mcp89_devinit_disable, +}; + +int +mcp89_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&mcp89_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c new file mode 100644 index 000000000..88bc890f8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" +#include "fbmem.h" + +#include +#include +#include +#include +#include + +static void +nv04_devinit_meminit(struct nvkm_devinit *init) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + u32 patt = 0xdeadbeef; + struct io_mapping *fb; + int i; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + /* Sequencer and refresh off */ + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) | 0x20); + nvkm_mask(device, NV04_PFB_DEBUG_0, 0, NV04_PFB_DEBUG_0_REFRESH_OFF); + + nvkm_mask(device, NV04_PFB_BOOT_0, ~0, + NV04_PFB_BOOT_0_RAM_AMOUNT_16MB | + NV04_PFB_BOOT_0_RAM_WIDTH_128 | + NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT); + + for (i = 0; i < 4; i++) + fbmem_poke(fb, 4 * i, patt); + + fbmem_poke(fb, 0x400000, patt + 1); + + if (fbmem_peek(fb, 0) == patt + 1) { + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_TYPE, + NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT); + nvkm_mask(device, NV04_PFB_DEBUG_0, + NV04_PFB_DEBUG_0_REFRESH_OFF, 0); + + for (i = 0; i < 4; i++) + fbmem_poke(fb, 4 * i, patt); + + if ((fbmem_peek(fb, 0xc) & 0xffff) != (patt & 0xffff)) + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_WIDTH_128 | + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + } else + if ((fbmem_peek(fb, 0xc) & 0xffff0000) != (patt & 0xffff0000)) { + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_WIDTH_128 | + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + } else + if (fbmem_peek(fb, 0) != patt) { + if (fbmem_readback(fb, 0x800000, patt)) + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + else + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_TYPE, + NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT); + } else + if (!fbmem_readback(fb, 0x800000, patt)) { + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + + } + + /* Refresh on, sequencer on */ + nvkm_mask(device, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0); + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) & ~0x20); + fbmem_fini(fb); +} + +static int +powerctrl_1_shift(int chip_version, int reg) +{ + int shift = -4; + + if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20) + return shift; + + switch (reg) { + case 0x680520: + shift += 4; fallthrough; + case 0x680508: + shift += 4; fallthrough; + case 0x680504: + shift += 4; fallthrough; + case 0x680500: + shift += 4; + } + + /* + * the shift for vpll regs is only used for nv3x chips with a single + * stage pll + */ + if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 || + chip_version == 0x36 || chip_version >= 0x40)) + shift = -4; + + return shift; +} + +void +setPLL_single(struct nvkm_devinit *init, u32 reg, + struct nvkm_pll_vals *pv) +{ + struct nvkm_device *device = init->subdev.device; + int chip_version = device->bios->version.chip; + uint32_t oldpll = nvkm_rd32(device, reg); + int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff; + uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1; + uint32_t saved_powerctrl_1 = 0; + int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg); + + if (oldpll == pll) + return; /* already set */ + + if (shift_powerctrl_1 >= 0) { + saved_powerctrl_1 = nvkm_rd32(device, 0x001584); + nvkm_wr32(device, 0x001584, + (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | + 1 << shift_powerctrl_1); + } + + if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1)) + /* upclock -- write new post divider first */ + nvkm_wr32(device, reg, pv->log2P << 16 | (oldpll & 0xffff)); + else + /* downclock -- write new NM first */ + nvkm_wr32(device, reg, (oldpll & 0xffff0000) | pv->NM1); + + if ((chip_version < 0x17 || chip_version == 0x1a) && + chip_version != 0x11) + /* wait a bit on older chips */ + msleep(64); + nvkm_rd32(device, reg); + + /* then write the other half as well */ + nvkm_wr32(device, reg, pll); + + if (shift_powerctrl_1 >= 0) + nvkm_wr32(device, 0x001584, saved_powerctrl_1); +} + +static uint32_t +new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580) +{ + bool head_a = (reg1 == 0x680508); + + if (ss) /* single stage pll mode */ + ramdac580 |= head_a ? 0x00000100 : 0x10000000; + else + ramdac580 &= head_a ? 0xfffffeff : 0xefffffff; + + return ramdac580; +} + +void +setPLL_double_highregs(struct nvkm_devinit *init, u32 reg1, + struct nvkm_pll_vals *pv) +{ + struct nvkm_device *device = init->subdev.device; + int chip_version = device->bios->version.chip; + bool nv3035 = chip_version == 0x30 || chip_version == 0x35; + uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70); + uint32_t oldpll1 = nvkm_rd32(device, reg1); + uint32_t oldpll2 = !nv3035 ? nvkm_rd32(device, reg2) : 0; + uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1; + uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2; + uint32_t oldramdac580 = 0, ramdac580 = 0; + bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */ + uint32_t saved_powerctrl_1 = 0, savedc040 = 0; + int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1); + + /* model specific additions to generic pll1 and pll2 set up above */ + if (nv3035) { + pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 | + (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4; + pll2 = 0; + } + if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */ + oldramdac580 = nvkm_rd32(device, 0x680580); + ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580); + if (oldramdac580 != ramdac580) + oldpll1 = ~0; /* force mismatch */ + if (single_stage) + /* magic value used by nvidia in single stage mode */ + pll2 |= 0x011f; + } + if (chip_version > 0x70) + /* magic bits set by the blob (but not the bios) on g71-73 */ + pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28; + + if (oldpll1 == pll1 && oldpll2 == pll2) + return; /* already set */ + + if (shift_powerctrl_1 >= 0) { + saved_powerctrl_1 = nvkm_rd32(device, 0x001584); + nvkm_wr32(device, 0x001584, + (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | + 1 << shift_powerctrl_1); + } + + if (chip_version >= 0x40) { + int shift_c040 = 14; + + switch (reg1) { + case 0x680504: + shift_c040 += 2; fallthrough; + case 0x680500: + shift_c040 += 2; fallthrough; + case 0x680520: + shift_c040 += 2; fallthrough; + case 0x680508: + shift_c040 += 2; + } + + savedc040 = nvkm_rd32(device, 0xc040); + if (shift_c040 != 14) + nvkm_wr32(device, 0xc040, savedc040 & ~(3 << shift_c040)); + } + + if (oldramdac580 != ramdac580) + nvkm_wr32(device, 0x680580, ramdac580); + + if (!nv3035) + nvkm_wr32(device, reg2, pll2); + nvkm_wr32(device, reg1, pll1); + + if (shift_powerctrl_1 >= 0) + nvkm_wr32(device, 0x001584, saved_powerctrl_1); + if (chip_version >= 0x40) + nvkm_wr32(device, 0xc040, savedc040); +} + +void +setPLL_double_lowregs(struct nvkm_devinit *init, u32 NMNMreg, + struct nvkm_pll_vals *pv) +{ + /* When setting PLLs, there is a merry game of disabling and enabling + * various bits of hardware during the process. This function is a + * synthesis of six nv4x traces, nearly each card doing a subtly + * different thing. With luck all the necessary bits for each card are + * combined herein. Without luck it deviates from each card's formula + * so as to not work on any :) + */ + struct nvkm_device *device = init->subdev.device; + uint32_t Preg = NMNMreg - 4; + bool mpll = Preg == 0x4020; + uint32_t oldPval = nvkm_rd32(device, Preg); + uint32_t NMNM = pv->NM2 << 16 | pv->NM1; + uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) | + 0xc << 28 | pv->log2P << 16; + uint32_t saved4600 = 0; + /* some cards have different maskc040s */ + uint32_t maskc040 = ~(3 << 14), savedc040; + bool single_stage = !pv->NM2 || pv->N2 == pv->M2; + + if (nvkm_rd32(device, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval) + return; + + if (Preg == 0x4000) + maskc040 = ~0x333; + if (Preg == 0x4058) + maskc040 = ~(0xc << 24); + + if (mpll) { + struct nvbios_pll info; + uint8_t Pval2; + + if (nvbios_pll_parse(device->bios, Preg, &info)) + return; + + Pval2 = pv->log2P + info.bias_p; + if (Pval2 > info.max_p) + Pval2 = info.max_p; + Pval |= 1 << 28 | Pval2 << 20; + + saved4600 = nvkm_rd32(device, 0x4600); + nvkm_wr32(device, 0x4600, saved4600 | 8 << 28); + } + if (single_stage) + Pval |= mpll ? 1 << 12 : 1 << 8; + + nvkm_wr32(device, Preg, oldPval | 1 << 28); + nvkm_wr32(device, Preg, Pval & ~(4 << 28)); + if (mpll) { + Pval |= 8 << 20; + nvkm_wr32(device, 0x4020, Pval & ~(0xc << 28)); + nvkm_wr32(device, 0x4038, Pval & ~(0xc << 28)); + } + + savedc040 = nvkm_rd32(device, 0xc040); + nvkm_wr32(device, 0xc040, savedc040 & maskc040); + + nvkm_wr32(device, NMNMreg, NMNM); + if (NMNMreg == 0x4024) + nvkm_wr32(device, 0x403c, NMNM); + + nvkm_wr32(device, Preg, Pval); + if (mpll) { + Pval &= ~(8 << 20); + nvkm_wr32(device, 0x4020, Pval); + nvkm_wr32(device, 0x4038, Pval); + nvkm_wr32(device, 0x4600, saved4600); + } + + nvkm_wr32(device, 0xc040, savedc040); + + if (mpll) { + nvkm_wr32(device, 0x4020, Pval & ~(1 << 28)); + nvkm_wr32(device, 0x4038, Pval & ~(1 << 28)); + } +} + +int +nv04_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &devinit->subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvkm_pll_vals pv; + struct nvbios_pll info; + int cv = bios->version.chip; + int N1, M1, N2, M2, P; + int ret; + + ret = nvbios_pll_parse(bios, type > 0x405c ? type : type - 4, &info); + if (ret) + return ret; + + ret = nv04_pll_calc(subdev, &info, freq, &N1, &M1, &N2, &M2, &P); + if (!ret) + return -EINVAL; + + pv.refclk = info.refclk; + pv.N1 = N1; + pv.M1 = M1; + pv.N2 = N2; + pv.M2 = M2; + pv.log2P = P; + + if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || + cv >= 0x40) { + if (type > 0x405c) + setPLL_double_highregs(devinit, type, &pv); + else + setPLL_double_lowregs(devinit, type, &pv); + } else + setPLL_single(devinit, type, &pv); + + return 0; +} + +int +nv04_devinit_post(struct nvkm_devinit *init, bool execute) +{ + return nvbios_post(&init->subdev, execute); +} + +void +nv04_devinit_preinit(struct nvkm_devinit *base) +{ + struct nv04_devinit *init = nv04_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + + /* make i2c busses accessible */ + nvkm_mask(device, 0x000200, 0x00000001, 0x00000001); + + /* unslave crtcs */ + if (init->owner < 0) + init->owner = nvkm_rdvgaowner(device); + nvkm_wrvgaowner(device, 0); + + if (!init->base.post) { + u32 htotal = nvkm_rdvgac(device, 0, 0x06); + htotal |= (nvkm_rdvgac(device, 0, 0x07) & 0x01) << 8; + htotal |= (nvkm_rdvgac(device, 0, 0x07) & 0x20) << 4; + htotal |= (nvkm_rdvgac(device, 0, 0x25) & 0x01) << 10; + htotal |= (nvkm_rdvgac(device, 0, 0x41) & 0x01) << 11; + if (!htotal) { + nvkm_debug(subdev, "adaptor not initialised\n"); + init->base.post = true; + } + } +} + +void * +nv04_devinit_dtor(struct nvkm_devinit *base) +{ + struct nv04_devinit *init = nv04_devinit(base); + /* restore vga owner saved at first init */ + nvkm_wrvgaowner(init->base.subdev.device, init->owner); + return init; +} + +int +nv04_devinit_new_(const struct nvkm_devinit_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_devinit **pinit) +{ + struct nv04_devinit *init; + + if (!(init = kzalloc(sizeof(*init), GFP_KERNEL))) + return -ENOMEM; + *pinit = &init->base; + + nvkm_devinit_ctor(func, device, type, inst, &init->base); + init->owner = -1; + return 0; +} + +static const struct nvkm_devinit_func +nv04_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv04_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv04_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv04_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h new file mode 100644 index 000000000..06ad8a606 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV04_DEVINIT_H__ +#define __NV04_DEVINIT_H__ +#define nv04_devinit(p) container_of((p), struct nv04_devinit, base) +#include "priv.h" +struct nvkm_pll_vals; + +struct nv04_devinit { + struct nvkm_devinit base; + int owner; +}; + +int nv04_devinit_new_(const struct nvkm_devinit_func *, struct nvkm_device *, + enum nvkm_subdev_type, int, struct nvkm_devinit **); +void *nv04_devinit_dtor(struct nvkm_devinit *); +void nv04_devinit_preinit(struct nvkm_devinit *); +void nv04_devinit_fini(struct nvkm_devinit *); +int nv04_devinit_pll_set(struct nvkm_devinit *, u32, u32); + +void setPLL_single(struct nvkm_devinit *, u32, struct nvkm_pll_vals *); +void setPLL_double_highregs(struct nvkm_devinit *, u32, struct nvkm_pll_vals *); +void setPLL_double_lowregs(struct nvkm_devinit *, u32, struct nvkm_pll_vals *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c new file mode 100644 index 000000000..1410befd2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" +#include "fbmem.h" + +#include +#include +#include +#include + +static void +nv05_devinit_meminit(struct nvkm_devinit *init) +{ + static const u8 default_config_tab[][2] = { + { 0x24, 0x00 }, + { 0x28, 0x00 }, + { 0x24, 0x01 }, + { 0x1f, 0x00 }, + { 0x0f, 0x00 }, + { 0x17, 0x00 }, + { 0x06, 0x00 }, + { 0x00, 0x00 } + }; + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct io_mapping *fb; + u32 patt = 0xdeadbeef; + u16 data; + u8 strap, ramcfg[2]; + int i, v; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + strap = (nvkm_rd32(device, 0x101000) & 0x0000003c) >> 2; + if ((data = bmp_mem_init_table(bios))) { + ramcfg[0] = nvbios_rd08(bios, data + 2 * strap + 0); + ramcfg[1] = nvbios_rd08(bios, data + 2 * strap + 1); + } else { + ramcfg[0] = default_config_tab[strap][0]; + ramcfg[1] = default_config_tab[strap][1]; + } + + /* Sequencer off */ + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) | 0x20); + + if (nvkm_rd32(device, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_UMA_ENABLE) + goto out; + + nvkm_mask(device, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0); + + /* If present load the hardcoded scrambling table */ + if (data) { + for (i = 0, data += 0x10; i < 8; i++, data += 4) { + u32 scramble = nvbios_rd32(bios, data); + nvkm_wr32(device, NV04_PFB_SCRAMBLE(i), scramble); + } + } + + /* Set memory type/width/length defaults depending on the straps */ + nvkm_mask(device, NV04_PFB_BOOT_0, 0x3f, ramcfg[0]); + + if (ramcfg[1] & 0x80) + nvkm_mask(device, NV04_PFB_CFG0, 0, NV04_PFB_CFG0_SCRAMBLE); + + nvkm_mask(device, NV04_PFB_CFG1, 0x700001, (ramcfg[1] & 1) << 20); + nvkm_mask(device, NV04_PFB_CFG1, 0, 1); + + /* Probe memory bus width */ + for (i = 0; i < 4; i++) + fbmem_poke(fb, 4 * i, patt); + + if (fbmem_peek(fb, 0xc) != patt) + nvkm_mask(device, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_WIDTH_128, 0); + + /* Probe memory length */ + v = nvkm_rd32(device, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_RAM_AMOUNT; + + if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_32MB && + (!fbmem_readback(fb, 0x1000000, ++patt) || + !fbmem_readback(fb, 0, ++patt))) + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_16MB); + + if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_16MB && + !fbmem_readback(fb, 0x800000, ++patt)) + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_8MB); + + if (!fbmem_readback(fb, 0x400000, ++patt)) + nvkm_mask(device, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + +out: + /* Sequencer on */ + nvkm_wrvgas(device, 0, 1, nvkm_rdvgas(device, 0, 1) & ~0x20); + fbmem_fini(fb); +} + +static const struct nvkm_devinit_func +nv05_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv05_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv05_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv05_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c new file mode 100644 index 000000000..a6aa8786d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" +#include "fbmem.h" + +#include +#include + +static void +nv10_devinit_meminit(struct nvkm_devinit *init) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + static const int mem_width[] = { 0x10, 0x00, 0x20 }; + int mem_width_count; + uint32_t patt = 0xdeadbeef; + struct io_mapping *fb; + int i, j, k; + + if (device->card_type >= NV_11 && device->chipset >= 0x17) + mem_width_count = 3; + else + mem_width_count = 2; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + nvkm_wr32(device, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); + + /* Probe memory bus width */ + for (i = 0; i < mem_width_count; i++) { + nvkm_mask(device, NV04_PFB_CFG0, 0x30, mem_width[i]); + + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) + fbmem_poke(fb, 0x1c, 0); + + fbmem_poke(fb, 0x1c, patt); + fbmem_poke(fb, 0x3c, 0); + + if (fbmem_peek(fb, 0x1c) == patt) + goto mem_width_found; + } + } + +mem_width_found: + patt <<= 1; + + /* Probe amount of installed memory */ + for (i = 0; i < 4; i++) { + int off = nvkm_rd32(device, 0x10020c) - 0x100000; + + fbmem_poke(fb, off, patt); + fbmem_poke(fb, 0, 0); + + fbmem_peek(fb, 0); + fbmem_peek(fb, 0); + fbmem_peek(fb, 0); + fbmem_peek(fb, 0); + + if (fbmem_peek(fb, off) == patt) + goto amount_found; + } + + /* IC missing - disable the upper half memory space. */ + nvkm_mask(device, NV04_PFB_CFG0, 0x1000, 0); + +amount_found: + fbmem_fini(fb); +} + +static const struct nvkm_devinit_func +nv10_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv10_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv10_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv10_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c new file mode 100644 index 000000000..4cc5ef9a5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv04.h" + +#include +#include + +static const struct nvkm_devinit_func +nv1a_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv1a_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv1a_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c new file mode 100644 index 000000000..67f46df72 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "nv04.h" +#include "fbmem.h" + +#include +#include + +static void +nv20_devinit_meminit(struct nvkm_devinit *init) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + uint32_t mask = (device->chipset >= 0x25 ? 0x300 : 0x900); + uint32_t amount, off; + struct io_mapping *fb; + + /* Map the framebuffer aperture */ + fb = fbmem_init(device); + if (!fb) { + nvkm_error(subdev, "failed to map fb\n"); + return; + } + + nvkm_wr32(device, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1); + + /* Allow full addressing */ + nvkm_mask(device, NV04_PFB_CFG0, 0, mask); + + amount = nvkm_rd32(device, 0x10020c); + for (off = amount; off > 0x2000000; off -= 0x2000000) + fbmem_poke(fb, off - 4, off); + + amount = nvkm_rd32(device, 0x10020c); + if (amount != fbmem_peek(fb, amount - 4)) + /* IC missing - disable the upper half memory space. */ + nvkm_mask(device, NV04_PFB_CFG0, mask, 0); + + fbmem_fini(fb); +} + +static const struct nvkm_devinit_func +nv20_devinit = { + .dtor = nv04_devinit_dtor, + .preinit = nv04_devinit_preinit, + .post = nv04_devinit_post, + .meminit = nv20_devinit_meminit, + .pll_set = nv04_devinit_pll_set, +}; + +int +nv20_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv04_devinit_new_(&nv20_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c new file mode 100644 index 000000000..380995d39 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c @@ -0,0 +1,178 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" + +#include +#include +#include +#include +#include +#include +#include + +int +nv50_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvbios_pll info; + int N1, M1, N2, M2, P; + int ret; + + ret = nvbios_pll_parse(bios, type, &info); + if (ret) { + nvkm_error(subdev, "failed to retrieve pll data, %d\n", ret); + return ret; + } + + ret = nv04_pll_calc(subdev, &info, freq, &N1, &M1, &N2, &M2, &P); + if (!ret) { + nvkm_error(subdev, "failed pll calculation\n"); + return -EINVAL; + } + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nvkm_wr32(device, info.reg + 0, 0x10000611); + nvkm_mask(device, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1); + nvkm_mask(device, info.reg + 8, 0x7fff00ff, (P << 28) | + (M2 << 16) | N2); + break; + case PLL_MEMORY: + nvkm_mask(device, info.reg + 0, 0x01ff0000, + (P << 22) | + (info.bias_p << 19) | + (P << 16)); + nvkm_wr32(device, info.reg + 4, (N1 << 8) | M1); + break; + default: + nvkm_mask(device, info.reg + 0, 0x00070000, (P << 16)); + nvkm_wr32(device, info.reg + 4, (N1 << 8) | M1); + break; + } + + return 0; +} + +static u64 +nv50_devinit_disable(struct nvkm_devinit *init) +{ + struct nvkm_device *device = init->subdev.device; + u32 r001540 = nvkm_rd32(device, 0x001540); + u64 disable = 0ULL; + + if (!(r001540 & 0x40000000)) + nvkm_subdev_disable(device, NVKM_ENGINE_MPEG, 0); + + return disable; +} + +void +nv50_devinit_preinit(struct nvkm_devinit *base) +{ + struct nvkm_subdev *subdev = &base->subdev; + struct nvkm_device *device = subdev->device; + + /* our heuristics can't detect whether the board has had its + * devinit scripts executed or not if the display engine is + * missing, assume it's a secondary gpu which requires post + */ + if (!base->post) { + nvkm_devinit_disable(base); + if (!device->disp) + base->post = true; + } + + /* magic to detect whether or not x86 vbios code has executed + * the devinit scripts to initialise the board + */ + if (!base->post) { + if (!nvkm_rdvgac(device, 0, 0x00) && + !nvkm_rdvgac(device, 0, 0x1a)) { + nvkm_debug(subdev, "adaptor not initialised\n"); + base->post = true; + } + } +} + +void +nv50_devinit_init(struct nvkm_devinit *base) +{ + struct nv50_devinit *init = nv50_devinit(base); + struct nvkm_subdev *subdev = &init->base.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvbios_outp info; + struct dcb_output outp; + u8 ver = 0xff, hdr, cnt, len; + int i = 0; + + /* if we ran the init tables, we have to execute the first script + * pointer of each dcb entry's display encoder table in order + * to properly initialise each encoder. + */ + while (init->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { + if (nvbios_outp_match(bios, outp.hasht, outp.hashm, + &ver, &hdr, &cnt, &len, &info)) { + nvbios_init(subdev, info.script[0], + init.outp = &outp; + init.or = ffs(outp.or) - 1; + init.link = outp.sorconf.link == 2; + ); + } + i++; + } +} + +int +nv50_devinit_new_(const struct nvkm_devinit_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_devinit **pinit) +{ + struct nv50_devinit *init; + + if (!(init = kzalloc(sizeof(*init), GFP_KERNEL))) + return -ENOMEM; + *pinit = &init->base; + + nvkm_devinit_ctor(func, device, type, inst, &init->base); + return 0; +} + +static const struct nvkm_devinit_func +nv50_devinit = { + .preinit = nv50_devinit_preinit, + .init = nv50_devinit_init, + .post = nv04_devinit_post, + .pll_set = nv50_devinit_pll_set, + .disable = nv50_devinit_disable, +}; + +int +nv50_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&nv50_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h new file mode 100644 index 000000000..987a7f478 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_DEVINIT_H__ +#define __NV50_DEVINIT_H__ +#define nv50_devinit(p) container_of((p), struct nv50_devinit, base) +#include "priv.h" + +struct nv50_devinit { + struct nvkm_devinit base; + u32 r001540; +}; + +int nv50_devinit_new_(const struct nvkm_devinit_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_devinit **); +void nv50_devinit_preinit(struct nvkm_devinit *); +void nv50_devinit_init(struct nvkm_devinit *); +int nv50_devinit_pll_set(struct nvkm_devinit *, u32, u32); + +int gt215_devinit_pll_set(struct nvkm_devinit *, u32, u32); + +int gf100_devinit_ctor(struct nvkm_object *, struct nvkm_object *, + struct nvkm_oclass *, void *, u32, + struct nvkm_object **); +int gf100_devinit_pll_set(struct nvkm_devinit *, u32, u32); +void gf100_devinit_preinit(struct nvkm_devinit *); + +u64 gm107_devinit_disable(struct nvkm_devinit *); + +int gm200_devinit_post(struct nvkm_devinit *, bool); +void gm200_devinit_preos(struct nv50_devinit *, bool); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h new file mode 100644 index 000000000..dd8b038a8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_DEVINIT_PRIV_H__ +#define __NVKM_DEVINIT_PRIV_H__ +#define nvkm_devinit(p) container_of((p), struct nvkm_devinit, subdev) +#include + +struct nvkm_devinit_func { + void *(*dtor)(struct nvkm_devinit *); + void (*preinit)(struct nvkm_devinit *); + void (*init)(struct nvkm_devinit *); + int (*post)(struct nvkm_devinit *, bool post); + u32 (*mmio)(struct nvkm_devinit *, u32); + void (*meminit)(struct nvkm_devinit *); + int (*pll_set)(struct nvkm_devinit *, u32 type, u32 freq); + u64 (*disable)(struct nvkm_devinit *); +}; + +void nvkm_devinit_ctor(const struct nvkm_devinit_func *, struct nvkm_device *, + enum nvkm_subdev_type, int inst, struct nvkm_devinit *); +u64 nvkm_devinit_disable(struct nvkm_devinit *); + +int nv04_devinit_post(struct nvkm_devinit *, bool); +int tu102_devinit_post(struct nvkm_devinit *, bool); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c new file mode 100644 index 000000000..81a1ad2c8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c @@ -0,0 +1,112 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nv50.h" + +#include +#include +#include + +static int +tu102_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) +{ + struct nvkm_subdev *subdev = &init->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_pll info; + int head = type - PLL_VPLL0; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(device->bios, type, &info); + if (ret) + return ret; + + ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nvkm_wr32(device, 0x00ef10 + (head * 0x40), fN << 16); + nvkm_wr32(device, 0x00ef04 + (head * 0x40), (P << 16) | + (N << 8) | + (M << 0)); + /*XXX*/ + nvkm_wr32(device, 0x00ef0c + (head * 0x40), 0x00000900); + nvkm_wr32(device, 0x00ef00 + (head * 0x40), 0x02000014); + break; + default: + nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +tu102_devinit_wait(struct nvkm_device *device) +{ + unsigned timeout = 50 + 2000; + + do { + if (nvkm_rd32(device, 0x118128) & 0x00000001) { + if ((nvkm_rd32(device, 0x118234) & 0x000000ff) == 0xff) + return 0; + } + + usleep_range(1000, 2000); + } while (timeout--); + + return -ETIMEDOUT; +} + +int +tu102_devinit_post(struct nvkm_devinit *base, bool post) +{ + struct nv50_devinit *init = nv50_devinit(base); + int ret; + + ret = tu102_devinit_wait(init->base.subdev.device); + if (ret) + return ret; + + gm200_devinit_preos(init, post); + return 0; +} + +static const struct nvkm_devinit_func +tu102_devinit = { + .init = nv50_devinit_init, + .post = tu102_devinit_post, + .pll_set = tu102_devinit_pll_set, + .disable = gm107_devinit_disable, +}; + +int +tu102_devinit_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_devinit **pinit) +{ + return nv50_devinit_new_(&tu102_devinit, device, type, inst, pinit); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/Kbuild new file mode 100644 index 000000000..d65ec719f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/Kbuild @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/fault/base.o +nvkm-y += nvkm/subdev/fault/user.o +nvkm-y += nvkm/subdev/fault/gp100.o +nvkm-y += nvkm/subdev/fault/gp10b.o +nvkm-y += nvkm/subdev/fault/gv100.o +nvkm-y += nvkm/subdev/fault/tu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c new file mode 100644 index 000000000..fd54fa504 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c @@ -0,0 +1,183 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include + +static void +nvkm_fault_ntfy_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fault *fault = container_of(event, typeof(*fault), event); + fault->func->buffer.intr(fault->buffer[index], false); +} + +static void +nvkm_fault_ntfy_init(struct nvkm_event *event, int type, int index) +{ + struct nvkm_fault *fault = container_of(event, typeof(*fault), event); + fault->func->buffer.intr(fault->buffer[index], true); +} + +static int +nvkm_fault_ntfy_ctor(struct nvkm_object *object, void *argv, u32 argc, + struct nvkm_notify *notify) +{ + struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object); + if (argc == 0) { + notify->size = 0; + notify->types = 1; + notify->index = buffer->id; + return 0; + } + return -ENOSYS; +} + +static const struct nvkm_event_func +nvkm_fault_ntfy = { + .ctor = nvkm_fault_ntfy_ctor, + .init = nvkm_fault_ntfy_init, + .fini = nvkm_fault_ntfy_fini, +}; + +static void +nvkm_fault_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_fault *fault = nvkm_fault(subdev); + return fault->func->intr(fault); +} + +static int +nvkm_fault_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_fault *fault = nvkm_fault(subdev); + if (fault->func->fini) + fault->func->fini(fault); + return 0; +} + +static int +nvkm_fault_init(struct nvkm_subdev *subdev) +{ + struct nvkm_fault *fault = nvkm_fault(subdev); + if (fault->func->init) + fault->func->init(fault); + return 0; +} + +static int +nvkm_fault_oneinit_buffer(struct nvkm_fault *fault, int id) +{ + struct nvkm_subdev *subdev = &fault->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fault_buffer *buffer; + int ret; + + if (!(buffer = kzalloc(sizeof(*buffer), GFP_KERNEL))) + return -ENOMEM; + buffer->fault = fault; + buffer->id = id; + fault->func->buffer.info(buffer); + fault->buffer[id] = buffer; + + nvkm_debug(subdev, "buffer %d: %d entries\n", id, buffer->entries); + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, buffer->entries * + fault->func->buffer.entry_size, 0x1000, true, + &buffer->mem); + if (ret) + return ret; + + /* Pin fault buffer in BAR2. */ + buffer->addr = fault->func->buffer.pin(buffer); + if (buffer->addr == ~0ULL) + return -EFAULT; + + return 0; +} + +static int +nvkm_fault_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_fault *fault = nvkm_fault(subdev); + int ret, i; + + for (i = 0; i < ARRAY_SIZE(fault->buffer); i++) { + if (i < fault->func->buffer.nr) { + ret = nvkm_fault_oneinit_buffer(fault, i); + if (ret) + return ret; + fault->buffer_nr = i + 1; + } + } + + ret = nvkm_event_init(&nvkm_fault_ntfy, 1, fault->buffer_nr, + &fault->event); + if (ret) + return ret; + + if (fault->func->oneinit) + ret = fault->func->oneinit(fault); + return ret; +} + +static void * +nvkm_fault_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_fault *fault = nvkm_fault(subdev); + int i; + + nvkm_notify_fini(&fault->nrpfb); + nvkm_event_fini(&fault->event); + + for (i = 0; i < fault->buffer_nr; i++) { + if (fault->buffer[i]) { + nvkm_memory_unref(&fault->buffer[i]->mem); + kfree(fault->buffer[i]); + } + } + + return fault; +} + +static const struct nvkm_subdev_func +nvkm_fault = { + .dtor = nvkm_fault_dtor, + .oneinit = nvkm_fault_oneinit, + .init = nvkm_fault_init, + .fini = nvkm_fault_fini, + .intr = nvkm_fault_intr, +}; + +int +nvkm_fault_new_(const struct nvkm_fault_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fault **pfault) +{ + struct nvkm_fault *fault; + if (!(fault = *pfault = kzalloc(sizeof(*fault), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_fault, device, type, inst, &fault->subdev); + fault->func = func; + fault->user.ctor = nvkm_ufault_new; + fault->user.base = func->user.base; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp100.c new file mode 100644 index 000000000..6af7959e0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp100.c @@ -0,0 +1,89 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include + +#include + +void +gp100_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + nvkm_mc_intr_mask(device, NVKM_SUBDEV_FAULT, 0, enable); +} + +void +gp100_fault_buffer_fini(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + nvkm_mask(device, 0x002a70, 0x00000001, 0x00000000); +} + +void +gp100_fault_buffer_init(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + nvkm_wr32(device, 0x002a74, upper_32_bits(buffer->addr)); + nvkm_wr32(device, 0x002a70, lower_32_bits(buffer->addr)); + nvkm_mask(device, 0x002a70, 0x00000001, 0x00000001); +} + +u64 gp100_fault_buffer_pin(struct nvkm_fault_buffer *buffer) +{ + return nvkm_memory_bar2(buffer->mem); +} + +void +gp100_fault_buffer_info(struct nvkm_fault_buffer *buffer) +{ + buffer->entries = nvkm_rd32(buffer->fault->subdev.device, 0x002a78); + buffer->get = 0x002a7c; + buffer->put = 0x002a80; +} + +void +gp100_fault_intr(struct nvkm_fault *fault) +{ + nvkm_event_send(&fault->event, 1, 0, NULL, 0); +} + +static const struct nvkm_fault_func +gp100_fault = { + .intr = gp100_fault_intr, + .buffer.nr = 1, + .buffer.entry_size = 32, + .buffer.info = gp100_fault_buffer_info, + .buffer.pin = gp100_fault_buffer_pin, + .buffer.init = gp100_fault_buffer_init, + .buffer.fini = gp100_fault_buffer_fini, + .buffer.intr = gp100_fault_buffer_intr, + .user = { { 0, 0, MAXWELL_FAULT_BUFFER_A }, 0 }, +}; + +int +gp100_fault_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fault **pfault) +{ + return nvkm_fault_new_(&gp100_fault, device, type, inst, pfault); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp10b.c new file mode 100644 index 000000000..89e0bc96f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp10b.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 NVIDIA Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "priv.h" + +#include + +#include + +u64 +gp10b_fault_buffer_pin(struct nvkm_fault_buffer *buffer) +{ + return nvkm_memory_addr(buffer->mem); +} + +static const struct nvkm_fault_func +gp10b_fault = { + .intr = gp100_fault_intr, + .buffer.nr = 1, + .buffer.entry_size = 32, + .buffer.info = gp100_fault_buffer_info, + .buffer.pin = gp10b_fault_buffer_pin, + .buffer.init = gp100_fault_buffer_init, + .buffer.fini = gp100_fault_buffer_fini, + .buffer.intr = gp100_fault_buffer_intr, + .user = { { 0, 0, MAXWELL_FAULT_BUFFER_A }, 0 }, +}; + +int +gp10b_fault_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fault **pfault) +{ + return nvkm_fault_new_(&gp10b_fault, device, type, inst, pfault); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gv100.c new file mode 100644 index 000000000..cd9d2ade5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/gv100.c @@ -0,0 +1,235 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include + +#include + +static void +gv100_fault_buffer_process(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + struct nvkm_memory *mem = buffer->mem; + u32 get = nvkm_rd32(device, buffer->get); + u32 put = nvkm_rd32(device, buffer->put); + if (put == get) + return; + + nvkm_kmap(mem); + while (get != put) { + const u32 base = get * buffer->fault->func->buffer.entry_size; + const u32 instlo = nvkm_ro32(mem, base + 0x00); + const u32 insthi = nvkm_ro32(mem, base + 0x04); + const u32 addrlo = nvkm_ro32(mem, base + 0x08); + const u32 addrhi = nvkm_ro32(mem, base + 0x0c); + const u32 timelo = nvkm_ro32(mem, base + 0x10); + const u32 timehi = nvkm_ro32(mem, base + 0x14); + const u32 info0 = nvkm_ro32(mem, base + 0x18); + const u32 info1 = nvkm_ro32(mem, base + 0x1c); + struct nvkm_fault_data info; + + if (++get == buffer->entries) + get = 0; + nvkm_wr32(device, buffer->get, get); + + info.addr = ((u64)addrhi << 32) | addrlo; + info.inst = ((u64)insthi << 32) | instlo; + info.time = ((u64)timehi << 32) | timelo; + info.engine = (info0 & 0x000000ff); + info.valid = (info1 & 0x80000000) >> 31; + info.gpc = (info1 & 0x1f000000) >> 24; + info.hub = (info1 & 0x00100000) >> 20; + info.access = (info1 & 0x000f0000) >> 16; + info.client = (info1 & 0x00007f00) >> 8; + info.reason = (info1 & 0x0000001f); + + nvkm_fifo_fault(device->fifo, &info); + } + nvkm_done(mem); +} + +static void +gv100_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + const u32 intr = buffer->id ? 0x08000000 : 0x20000000; + if (enable) + nvkm_mask(device, 0x100a2c, intr, intr); + else + nvkm_mask(device, 0x100a34, intr, intr); +} + +static void +gv100_fault_buffer_fini(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + const u32 foff = buffer->id * 0x14; + nvkm_mask(device, 0x100e34 + foff, 0x80000000, 0x00000000); +} + +static void +gv100_fault_buffer_init(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + const u32 foff = buffer->id * 0x14; + + nvkm_mask(device, 0x100e34 + foff, 0xc0000000, 0x40000000); + nvkm_wr32(device, 0x100e28 + foff, upper_32_bits(buffer->addr)); + nvkm_wr32(device, 0x100e24 + foff, lower_32_bits(buffer->addr)); + nvkm_mask(device, 0x100e34 + foff, 0x80000000, 0x80000000); +} + +static void +gv100_fault_buffer_info(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + const u32 foff = buffer->id * 0x14; + + nvkm_mask(device, 0x100e34 + foff, 0x40000000, 0x40000000); + + buffer->entries = nvkm_rd32(device, 0x100e34 + foff) & 0x000fffff; + buffer->get = 0x100e2c + foff; + buffer->put = 0x100e30 + foff; +} + +static int +gv100_fault_ntfy_nrpfb(struct nvkm_notify *notify) +{ + struct nvkm_fault *fault = container_of(notify, typeof(*fault), nrpfb); + gv100_fault_buffer_process(fault->buffer[0]); + return NVKM_NOTIFY_KEEP; +} + +static void +gv100_fault_intr_fault(struct nvkm_fault *fault) +{ + struct nvkm_subdev *subdev = &fault->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fault_data info; + const u32 addrlo = nvkm_rd32(device, 0x100e4c); + const u32 addrhi = nvkm_rd32(device, 0x100e50); + const u32 info0 = nvkm_rd32(device, 0x100e54); + const u32 insthi = nvkm_rd32(device, 0x100e58); + const u32 info1 = nvkm_rd32(device, 0x100e5c); + + info.addr = ((u64)addrhi << 32) | addrlo; + info.inst = ((u64)insthi << 32) | (info0 & 0xfffff000); + info.time = 0; + info.engine = (info0 & 0x000000ff); + info.valid = (info1 & 0x80000000) >> 31; + info.gpc = (info1 & 0x1f000000) >> 24; + info.hub = (info1 & 0x00100000) >> 20; + info.access = (info1 & 0x000f0000) >> 16; + info.client = (info1 & 0x00007f00) >> 8; + info.reason = (info1 & 0x0000001f); + + nvkm_fifo_fault(device->fifo, &info); +} + +static void +gv100_fault_intr(struct nvkm_fault *fault) +{ + struct nvkm_subdev *subdev = &fault->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x100a20); + + if (stat & 0x80000000) { + gv100_fault_intr_fault(fault); + nvkm_wr32(device, 0x100e60, 0x80000000); + stat &= ~0x80000000; + } + + if (stat & 0x20000000) { + if (fault->buffer[0]) { + nvkm_event_send(&fault->event, 1, 0, NULL, 0); + stat &= ~0x20000000; + } + } + + if (stat & 0x08000000) { + if (fault->buffer[1]) { + nvkm_event_send(&fault->event, 1, 1, NULL, 0); + stat &= ~0x08000000; + } + } + + if (stat) { + nvkm_debug(subdev, "intr %08x\n", stat); + } +} + +static void +gv100_fault_fini(struct nvkm_fault *fault) +{ + nvkm_notify_put(&fault->nrpfb); + if (fault->buffer[0]) + fault->func->buffer.fini(fault->buffer[0]); + nvkm_mask(fault->subdev.device, 0x100a34, 0x80000000, 0x80000000); +} + +static void +gv100_fault_init(struct nvkm_fault *fault) +{ + nvkm_mask(fault->subdev.device, 0x100a2c, 0x80000000, 0x80000000); + fault->func->buffer.init(fault->buffer[0]); + nvkm_notify_get(&fault->nrpfb); +} + +int +gv100_fault_oneinit(struct nvkm_fault *fault) +{ + return nvkm_notify_init(&fault->buffer[0]->object, &fault->event, + gv100_fault_ntfy_nrpfb, true, NULL, 0, 0, + &fault->nrpfb); +} + +static const struct nvkm_fault_func +gv100_fault = { + .oneinit = gv100_fault_oneinit, + .init = gv100_fault_init, + .fini = gv100_fault_fini, + .intr = gv100_fault_intr, + .buffer.nr = 2, + .buffer.entry_size = 32, + .buffer.info = gv100_fault_buffer_info, + .buffer.pin = gp100_fault_buffer_pin, + .buffer.init = gv100_fault_buffer_init, + .buffer.fini = gv100_fault_buffer_fini, + .buffer.intr = gv100_fault_buffer_intr, + /*TODO: Figure out how to expose non-replayable fault buffer, which, + * for some reason, is where recoverable CE faults appear... + * + * It's a bit tricky, as both NVKM and SVM will need access to + * the non-replayable fault buffer. + */ + .user = { { 0, 0, VOLTA_FAULT_BUFFER_A }, 1 }, +}; + +int +gv100_fault_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fault **pfault) +{ + return nvkm_fault_new_(&gv100_fault, device, type, inst, pfault); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/priv.h new file mode 100644 index 000000000..36681c347 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/priv.h @@ -0,0 +1,57 @@ +#ifndef __NVKM_FAULT_PRIV_H__ +#define __NVKM_FAULT_PRIV_H__ +#define nvkm_fault_buffer(p) container_of((p), struct nvkm_fault_buffer, object) +#define nvkm_fault(p) container_of((p), struct nvkm_fault, subdev) +#include + +#include +#include + +struct nvkm_fault_buffer { + struct nvkm_object object; + struct nvkm_fault *fault; + int id; + int entries; + u32 get; + u32 put; + struct nvkm_memory *mem; + u64 addr; +}; + +int nvkm_fault_new_(const struct nvkm_fault_func *, struct nvkm_device *, enum nvkm_subdev_type, + int inst, struct nvkm_fault **); + +struct nvkm_fault_func { + int (*oneinit)(struct nvkm_fault *); + void (*init)(struct nvkm_fault *); + void (*fini)(struct nvkm_fault *); + void (*intr)(struct nvkm_fault *); + struct { + int nr; + u32 entry_size; + void (*info)(struct nvkm_fault_buffer *); + u64 (*pin)(struct nvkm_fault_buffer *); + void (*init)(struct nvkm_fault_buffer *); + void (*fini)(struct nvkm_fault_buffer *); + void (*intr)(struct nvkm_fault_buffer *, bool enable); + } buffer; + struct { + struct nvkm_sclass base; + int rp; + } user; +}; + +void gp100_fault_buffer_intr(struct nvkm_fault_buffer *, bool enable); +void gp100_fault_buffer_fini(struct nvkm_fault_buffer *); +void gp100_fault_buffer_init(struct nvkm_fault_buffer *); +u64 gp100_fault_buffer_pin(struct nvkm_fault_buffer *); +void gp100_fault_buffer_info(struct nvkm_fault_buffer *); +void gp100_fault_intr(struct nvkm_fault *); + +u64 gp10b_fault_buffer_pin(struct nvkm_fault_buffer *); + +int gv100_fault_oneinit(struct nvkm_fault *); + +int nvkm_ufault_new(struct nvkm_device *, const struct nvkm_oclass *, + void *, u32, struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/tu102.c new file mode 100644 index 000000000..91eb6729c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/tu102.c @@ -0,0 +1,188 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include +#include +#include + +#include + +static void +tu102_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable) +{ + /*XXX: Earlier versions of RM touched the old regs on Turing, + * which don't appear to actually work anymore, but newer + * versions of RM don't appear to touch anything at all.. + */ + struct nvkm_device *device = buffer->fault->subdev.device; + + nvkm_mc_intr_mask(device, NVKM_SUBDEV_FAULT, 0, enable); +} + +static void +tu102_fault_buffer_fini(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + const u32 foff = buffer->id * 0x20; + + /* Disable the fault interrupts */ + nvkm_wr32(device, 0xb81408, 0x1); + nvkm_wr32(device, 0xb81410, 0x10); + + nvkm_mask(device, 0xb83010 + foff, 0x80000000, 0x00000000); +} + +static void +tu102_fault_buffer_init(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + const u32 foff = buffer->id * 0x20; + + /* Enable the fault interrupts */ + nvkm_wr32(device, 0xb81208, 0x1); + nvkm_wr32(device, 0xb81210, 0x10); + + nvkm_mask(device, 0xb83010 + foff, 0xc0000000, 0x40000000); + nvkm_wr32(device, 0xb83004 + foff, upper_32_bits(buffer->addr)); + nvkm_wr32(device, 0xb83000 + foff, lower_32_bits(buffer->addr)); + nvkm_mask(device, 0xb83010 + foff, 0x80000000, 0x80000000); +} + +static void +tu102_fault_buffer_info(struct nvkm_fault_buffer *buffer) +{ + struct nvkm_device *device = buffer->fault->subdev.device; + const u32 foff = buffer->id * 0x20; + + nvkm_mask(device, 0xb83010 + foff, 0x40000000, 0x40000000); + + buffer->entries = nvkm_rd32(device, 0xb83010 + foff) & 0x000fffff; + buffer->get = 0xb83008 + foff; + buffer->put = 0xb8300c + foff; +} + +static void +tu102_fault_intr_fault(struct nvkm_fault *fault) +{ + struct nvkm_subdev *subdev = &fault->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fault_data info; + const u32 addrlo = nvkm_rd32(device, 0xb83080); + const u32 addrhi = nvkm_rd32(device, 0xb83084); + const u32 info0 = nvkm_rd32(device, 0xb83088); + const u32 insthi = nvkm_rd32(device, 0xb8308c); + const u32 info1 = nvkm_rd32(device, 0xb83090); + + info.addr = ((u64)addrhi << 32) | addrlo; + info.inst = ((u64)insthi << 32) | (info0 & 0xfffff000); + info.time = 0; + info.engine = (info0 & 0x000000ff); + info.valid = (info1 & 0x80000000) >> 31; + info.gpc = (info1 & 0x1f000000) >> 24; + info.hub = (info1 & 0x00100000) >> 20; + info.access = (info1 & 0x000f0000) >> 16; + info.client = (info1 & 0x00007f00) >> 8; + info.reason = (info1 & 0x0000001f); + + nvkm_fifo_fault(device->fifo, &info); +} + +static void +tu102_fault_intr(struct nvkm_fault *fault) +{ + struct nvkm_subdev *subdev = &fault->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0xb83094); + + if (stat & 0x80000000) { + tu102_fault_intr_fault(fault); + nvkm_wr32(device, 0xb83094, 0x80000000); + stat &= ~0x80000000; + } + + if (stat & 0x00000200) { + /* Clear the associated interrupt flag */ + nvkm_wr32(device, 0xb81010, 0x10); + + if (fault->buffer[0]) { + nvkm_event_send(&fault->event, 1, 0, NULL, 0); + stat &= ~0x00000200; + } + } + + /* Replayable MMU fault */ + if (stat & 0x00000100) { + /* Clear the associated interrupt flag */ + nvkm_wr32(device, 0xb81008, 0x1); + + if (fault->buffer[1]) { + nvkm_event_send(&fault->event, 1, 1, NULL, 0); + stat &= ~0x00000100; + } + } + + if (stat) { + nvkm_debug(subdev, "intr %08x\n", stat); + } +} + +static void +tu102_fault_fini(struct nvkm_fault *fault) +{ + nvkm_notify_put(&fault->nrpfb); + if (fault->buffer[0]) + fault->func->buffer.fini(fault->buffer[0]); + /*XXX: disable priv faults */ +} + +static void +tu102_fault_init(struct nvkm_fault *fault) +{ + /*XXX: enable priv faults */ + fault->func->buffer.init(fault->buffer[0]); + nvkm_notify_get(&fault->nrpfb); +} + +static const struct nvkm_fault_func +tu102_fault = { + .oneinit = gv100_fault_oneinit, + .init = tu102_fault_init, + .fini = tu102_fault_fini, + .intr = tu102_fault_intr, + .buffer.nr = 2, + .buffer.entry_size = 32, + .buffer.info = tu102_fault_buffer_info, + .buffer.pin = gp100_fault_buffer_pin, + .buffer.init = tu102_fault_buffer_init, + .buffer.fini = tu102_fault_buffer_fini, + .buffer.intr = tu102_fault_buffer_intr, + .user = { { 0, 0, VOLTA_FAULT_BUFFER_A }, 1 }, +}; + +int +tu102_fault_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fault **pfault) +{ + return nvkm_fault_new_(&tu102_fault, device, type, inst, pfault); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c new file mode 100644 index 000000000..ac835c958 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c @@ -0,0 +1,106 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include + +#include +#include + +static int +nvkm_ufault_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *addr, u64 *size) +{ + struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object); + struct nvkm_device *device = buffer->fault->subdev.device; + *type = NVKM_OBJECT_MAP_IO; + *addr = device->func->resource_addr(device, 3) + buffer->addr; + *size = nvkm_memory_size(buffer->mem); + return 0; +} + +static int +nvkm_ufault_ntfy(struct nvkm_object *object, u32 type, + struct nvkm_event **pevent) +{ + struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object); + if (type == NVB069_V0_NTFY_FAULT) { + *pevent = &buffer->fault->event; + return 0; + } + return -EINVAL; +} + +static int +nvkm_ufault_fini(struct nvkm_object *object, bool suspend) +{ + struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object); + buffer->fault->func->buffer.fini(buffer); + return 0; +} + +static int +nvkm_ufault_init(struct nvkm_object *object) +{ + struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object); + buffer->fault->func->buffer.init(buffer); + return 0; +} + +static void * +nvkm_ufault_dtor(struct nvkm_object *object) +{ + return NULL; +} + +static const struct nvkm_object_func +nvkm_ufault = { + .dtor = nvkm_ufault_dtor, + .init = nvkm_ufault_init, + .fini = nvkm_ufault_fini, + .ntfy = nvkm_ufault_ntfy, + .map = nvkm_ufault_map, +}; + +int +nvkm_ufault_new(struct nvkm_device *device, const struct nvkm_oclass *oclass, + void *argv, u32 argc, struct nvkm_object **pobject) +{ + union { + struct nvif_clb069_v0 v0; + } *args = argv; + struct nvkm_fault *fault = device->fault; + struct nvkm_fault_buffer *buffer = fault->buffer[fault->func->user.rp]; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + args->v0.entries = buffer->entries; + args->v0.get = buffer->get; + args->v0.put = buffer->put; + } else + return ret; + + nvkm_object_ctor(&nvkm_ufault, oclass, &buffer->object); + *pobject = &buffer->object; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild new file mode 100644 index 000000000..5d0bab8ec --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/fb/base.o +nvkm-y += nvkm/subdev/fb/nv04.o +nvkm-y += nvkm/subdev/fb/nv10.o +nvkm-y += nvkm/subdev/fb/nv1a.o +nvkm-y += nvkm/subdev/fb/nv20.o +nvkm-y += nvkm/subdev/fb/nv25.o +nvkm-y += nvkm/subdev/fb/nv30.o +nvkm-y += nvkm/subdev/fb/nv35.o +nvkm-y += nvkm/subdev/fb/nv36.o +nvkm-y += nvkm/subdev/fb/nv40.o +nvkm-y += nvkm/subdev/fb/nv41.o +nvkm-y += nvkm/subdev/fb/nv44.o +nvkm-y += nvkm/subdev/fb/nv46.o +nvkm-y += nvkm/subdev/fb/nv47.o +nvkm-y += nvkm/subdev/fb/nv49.o +nvkm-y += nvkm/subdev/fb/nv4e.o +nvkm-y += nvkm/subdev/fb/nv50.o +nvkm-y += nvkm/subdev/fb/g84.o +nvkm-y += nvkm/subdev/fb/gt215.o +nvkm-y += nvkm/subdev/fb/mcp77.o +nvkm-y += nvkm/subdev/fb/mcp89.o +nvkm-y += nvkm/subdev/fb/gf100.o +nvkm-y += nvkm/subdev/fb/gf108.o +nvkm-y += nvkm/subdev/fb/gk104.o +nvkm-y += nvkm/subdev/fb/gk110.o +nvkm-y += nvkm/subdev/fb/gk20a.o +nvkm-y += nvkm/subdev/fb/gm107.o +nvkm-y += nvkm/subdev/fb/gm200.o +nvkm-y += nvkm/subdev/fb/gm20b.o +nvkm-y += nvkm/subdev/fb/gp100.o +nvkm-y += nvkm/subdev/fb/gp102.o +nvkm-y += nvkm/subdev/fb/gp10b.o +nvkm-y += nvkm/subdev/fb/gv100.o +nvkm-y += nvkm/subdev/fb/ga100.o +nvkm-y += nvkm/subdev/fb/ga102.o + +nvkm-y += nvkm/subdev/fb/ram.o +nvkm-y += nvkm/subdev/fb/ramnv04.o +nvkm-y += nvkm/subdev/fb/ramnv10.o +nvkm-y += nvkm/subdev/fb/ramnv1a.o +nvkm-y += nvkm/subdev/fb/ramnv20.o +nvkm-y += nvkm/subdev/fb/ramnv40.o +nvkm-y += nvkm/subdev/fb/ramnv41.o +nvkm-y += nvkm/subdev/fb/ramnv44.o +nvkm-y += nvkm/subdev/fb/ramnv49.o +nvkm-y += nvkm/subdev/fb/ramnv4e.o +nvkm-y += nvkm/subdev/fb/ramnv50.o +nvkm-y += nvkm/subdev/fb/ramgt215.o +nvkm-y += nvkm/subdev/fb/rammcp77.o +nvkm-y += nvkm/subdev/fb/ramgf100.o +nvkm-y += nvkm/subdev/fb/ramgf108.o +nvkm-y += nvkm/subdev/fb/ramgk104.o +nvkm-y += nvkm/subdev/fb/ramgm107.o +nvkm-y += nvkm/subdev/fb/ramgm200.o +nvkm-y += nvkm/subdev/fb/ramgp100.o +nvkm-y += nvkm/subdev/fb/ramga102.o +nvkm-y += nvkm/subdev/fb/sddr2.o +nvkm-y += nvkm/subdev/fb/sddr3.o +nvkm-y += nvkm/subdev/fb/gddr3.o +nvkm-y += nvkm/subdev/fb/gddr5.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c new file mode 100644 index 000000000..6faaea948 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c @@ -0,0 +1,247 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "ram.h" + +#include +#include +#include +#include +#include +#include + +void +nvkm_fb_tile_fini(struct nvkm_fb *fb, int region, struct nvkm_fb_tile *tile) +{ + fb->func->tile.fini(fb, region, tile); +} + +void +nvkm_fb_tile_init(struct nvkm_fb *fb, int region, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *tile) +{ + fb->func->tile.init(fb, region, addr, size, pitch, flags, tile); +} + +void +nvkm_fb_tile_prog(struct nvkm_fb *fb, int region, struct nvkm_fb_tile *tile) +{ + struct nvkm_device *device = fb->subdev.device; + if (fb->func->tile.prog) { + fb->func->tile.prog(fb, region, tile); + if (device->gr) + nvkm_engine_tile(&device->gr->engine, region); + if (device->mpeg) + nvkm_engine_tile(device->mpeg, region); + } +} + +int +nvkm_fb_bios_memtype(struct nvkm_bios *bios) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvkm_device *device = subdev->device; + const u8 ramcfg = (nvkm_rd32(device, 0x101000) & 0x0000003c) >> 2; + struct nvbios_M0203E M0203E; + u8 ver, hdr; + + if (nvbios_M0203Em(bios, ramcfg, &ver, &hdr, &M0203E)) { + switch (M0203E.type) { + case M0203E_TYPE_DDR2 : return NVKM_RAM_TYPE_DDR2; + case M0203E_TYPE_DDR3 : return NVKM_RAM_TYPE_DDR3; + case M0203E_TYPE_GDDR3 : return NVKM_RAM_TYPE_GDDR3; + case M0203E_TYPE_GDDR5 : return NVKM_RAM_TYPE_GDDR5; + case M0203E_TYPE_GDDR5X: return NVKM_RAM_TYPE_GDDR5X; + case M0203E_TYPE_GDDR6 : return NVKM_RAM_TYPE_GDDR6; + case M0203E_TYPE_HBM2 : return NVKM_RAM_TYPE_HBM2; + default: + nvkm_warn(subdev, "M0203E type %02x\n", M0203E.type); + return NVKM_RAM_TYPE_UNKNOWN; + } + } + + nvkm_warn(subdev, "M0203E not matched!\n"); + return NVKM_RAM_TYPE_UNKNOWN; +} + +static void +nvkm_fb_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_fb *fb = nvkm_fb(subdev); + if (fb->func->intr) + fb->func->intr(fb); +} + +static int +nvkm_fb_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_fb *fb = nvkm_fb(subdev); + u32 tags = 0; + + if (fb->func->ram_new) { + int ret = fb->func->ram_new(fb, &fb->ram); + if (ret) { + nvkm_error(subdev, "vram setup failed, %d\n", ret); + return ret; + } + } + + if (fb->func->oneinit) { + int ret = fb->func->oneinit(fb); + if (ret) + return ret; + } + + /* Initialise compression tag allocator. + * + * LTC oneinit() will override this on Fermi and newer. + */ + if (fb->func->tags) { + tags = fb->func->tags(fb); + nvkm_debug(subdev, "%d comptags\n", tags); + } + + return nvkm_mm_init(&fb->tags.mm, 0, 0, tags, 1); +} + +static int +nvkm_fb_init_scrub_vpr(struct nvkm_fb *fb) +{ + struct nvkm_subdev *subdev = &fb->subdev; + int ret; + + nvkm_debug(subdev, "VPR locked, running scrubber binary\n"); + + if (!fb->vpr_scrubber.size) { + nvkm_warn(subdev, "VPR locked, but no scrubber binary!\n"); + return 0; + } + + ret = fb->func->vpr.scrub(fb); + if (ret) { + nvkm_error(subdev, "VPR scrubber binary failed\n"); + return ret; + } + + if (fb->func->vpr.scrub_required(fb)) { + nvkm_error(subdev, "VPR still locked after scrub!\n"); + return -EIO; + } + + nvkm_debug(subdev, "VPR scrubber binary successful\n"); + return 0; +} + +static int +nvkm_fb_init(struct nvkm_subdev *subdev) +{ + struct nvkm_fb *fb = nvkm_fb(subdev); + int ret, i; + + if (fb->ram) { + ret = nvkm_ram_init(fb->ram); + if (ret) + return ret; + } + + for (i = 0; i < fb->tile.regions; i++) + fb->func->tile.prog(fb, i, &fb->tile.region[i]); + + if (fb->func->init) + fb->func->init(fb); + + if (fb->func->init_remapper) + fb->func->init_remapper(fb); + + if (fb->func->init_page) { + ret = fb->func->init_page(fb); + if (WARN_ON(ret)) + return ret; + } + + if (fb->func->init_unkn) + fb->func->init_unkn(fb); + + if (fb->func->vpr.scrub_required && + fb->func->vpr.scrub_required(fb)) { + ret = nvkm_fb_init_scrub_vpr(fb); + if (ret) + return ret; + } + + return 0; +} + +static void * +nvkm_fb_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_fb *fb = nvkm_fb(subdev); + int i; + + nvkm_memory_unref(&fb->mmu_wr); + nvkm_memory_unref(&fb->mmu_rd); + + for (i = 0; i < fb->tile.regions; i++) + fb->func->tile.fini(fb, i, &fb->tile.region[i]); + + nvkm_mm_fini(&fb->tags.mm); + mutex_destroy(&fb->tags.mutex); + + nvkm_ram_del(&fb->ram); + + nvkm_blob_dtor(&fb->vpr_scrubber); + + if (fb->func->dtor) + return fb->func->dtor(fb); + return fb; +} + +static const struct nvkm_subdev_func +nvkm_fb = { + .dtor = nvkm_fb_dtor, + .oneinit = nvkm_fb_oneinit, + .init = nvkm_fb_init, + .intr = nvkm_fb_intr, +}; + +void +nvkm_fb_ctor(const struct nvkm_fb_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fb *fb) +{ + nvkm_subdev_ctor(&nvkm_fb, device, type, inst, &fb->subdev); + fb->func = func; + fb->tile.regions = fb->func->tile.regions; + fb->page = nvkm_longopt(device->cfgopt, "NvFbBigPage", fb->func->default_bigpage); + mutex_init(&fb->tags.mutex); +} + +int +nvkm_fb_new_(const struct nvkm_fb_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + if (!(*pfb = kzalloc(sizeof(**pfb), GFP_KERNEL))) + return -ENOMEM; + nvkm_fb_ctor(func, device, type, inst, *pfb); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c new file mode 100644 index 000000000..770a4ad39 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "ram.h" + +static const struct nv50_fb_func +g84_fb = { + .ram_new = nv50_ram_new, + .tags = nv20_fb_tags, + .trap = 0x001d07ff, +}; + +int +g84_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nv50_fb_new_(&g84_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c new file mode 100644 index 000000000..b47bebfbc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ram.h" + +static const struct nvkm_fb_func +ga100_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gp100_fb_init, + .init_page = gv100_fb_init_page, + .init_unkn = gp100_fb_init_unkn, + .ram_new = gp100_ram_new, + .default_bigpage = 16, +}; + +int +ga100_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gp102_fb_new_(&ga100_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c new file mode 100644 index 000000000..6ea7908f0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ram.h" + +static const struct nvkm_fb_func +ga102_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gp100_fb_init, + .init_page = gv100_fb_init_page, + .init_unkn = gp100_fb_init_unkn, + .ram_new = ga102_ram_new, + .default_bigpage = 16, +}; + +int +ga102_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gp102_fb_new_(&ga102_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c new file mode 100644 index 000000000..1d2d6bae7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c @@ -0,0 +1,119 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Roy Spliet + */ +#include "ram.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramgddr3_cl_lo[] = { + { 5, 5 }, { 7, 7 }, { 8, 0 }, { 9, 1 }, { 10, 2 }, { 11, 3 }, { 12, 8 }, + /* the below are mentioned in some, but not all, gddr3 docs */ + { 13, 9 }, { 14, 6 }, + /* XXX: Per Samsung docs, are these used? They overlap with Qimonda */ + /* { 4, 4 }, { 5, 5 }, { 6, 6 }, { 12, 8 }, { 13, 9 }, { 14, 10 }, + * { 15, 11 }, */ + { -1 } +}; + +static const struct ramxlat +ramgddr3_cl_hi[] = { + { 10, 2 }, { 11, 3 }, { 12, 4 }, { 13, 5 }, { 14, 6 }, { 15, 7 }, + { 16, 0 }, { 17, 1 }, + { -1 } +}; + +static const struct ramxlat +ramgddr3_wr_lo[] = { + { 5, 2 }, { 7, 4 }, { 8, 5 }, { 9, 6 }, { 10, 7 }, + { 11, 0 }, { 13 , 1 }, + /* the below are mentioned in some, but not all, gddr3 docs */ + { 4, 0 }, { 6, 3 }, { 12, 1 }, + { -1 } +}; + +int +nvkm_gddr3_calc(struct nvkm_ram *ram) +{ + int CL, WR, CWL, DLL = 0, ODT = 0, RON, hi; + + switch (ram->next->bios.timing_ver) { + case 0x10: + CWL = ram->next->bios.timing_10_CWL; + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_DLLoff; + ODT = ram->next->bios.timing_10_ODT; + RON = ram->next->bios.ramcfg_RON; + break; + case 0x20: + CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0; + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + /* XXX: Get these values from the VBIOS instead */ + DLL = !(ram->mr[1] & 0x1); + RON = !((ram->mr[1] & 0x300) >> 8); + break; + default: + return -ENOSYS; + } + + if (ram->next->bios.timing_ver == 0x20 || + ram->next->bios.ramcfg_timing == 0xff) { + ODT = (ram->mr[1] & 0xc) >> 2; + } + + hi = ram->mr[2] & 0x1; + CL = ramxlat(hi ? ramgddr3_cl_hi : ramgddr3_cl_lo, CL); + WR = ramxlat(ramgddr3_wr_lo, WR); + if (CL < 0 || CWL < 1 || CWL > 7 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf74; + ram->mr[0] |= (CWL & 0x07) << 9; + ram->mr[0] |= (CL & 0x07) << 4; + ram->mr[0] |= (CL & 0x08) >> 1; + + ram->mr[1] &= ~0x3fc; + ram->mr[1] |= (ODT & 0x03) << 2; + ram->mr[1] |= (RON & 0x03) << 8; + ram->mr[1] |= (WR & 0x03) << 4; + ram->mr[1] |= (WR & 0x04) << 5; + ram->mr[1] |= !DLL << 6; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c new file mode 100644 index 000000000..2cc074d39 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c @@ -0,0 +1,121 @@ +/* + * 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 "ram.h" + +/* binary driver only executes this path if the condition (a) is true + * for any configuration (combination of rammap+ramcfg+timing) that + * can be reached on a given card. for now, we will execute the branch + * unconditionally in the hope that a "false everywhere" in the bios + * tables doesn't actually mean "don't touch this". + */ +#define NOTE00(a) 1 + +int +nvkm_gddr5_calc(struct nvkm_ram *ram, bool nuts) +{ + int pd, lf, xd, vh, vr, vo, l3; + int WL, CL, WR, at[2], dt, ds; + int rq = ram->freq < 1000000; /* XXX */ + + xd = !ram->next->bios.ramcfg_DLLoff; + + switch (ram->next->bios.ramcfg_ver) { + case 0x11: + pd = ram->next->bios.ramcfg_11_01_80; + lf = ram->next->bios.ramcfg_11_01_40; + vh = ram->next->bios.ramcfg_11_02_10; + vr = ram->next->bios.ramcfg_11_02_04; + vo = ram->next->bios.ramcfg_11_06; + l3 = !ram->next->bios.ramcfg_11_07_02; + break; + default: + return -ENOSYS; + } + + switch (ram->next->bios.timing_ver) { + case 0x20: + WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f); + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + at[0] = ram->next->bios.timing_20_2e_c0; + at[1] = ram->next->bios.timing_20_2e_30; + dt = ram->next->bios.timing_20_2e_03; + ds = ram->next->bios.timing_20_2f_03; + break; + default: + return -ENOSYS; + } + + if (WL < 1 || WL > 7 || CL < 5 || CL > 36 || WR < 4 || WR > 35) + return -EINVAL; + CL -= 5; + WR -= 4; + + ram->mr[0] &= ~0xf7f; + ram->mr[0] |= (WR & 0x0f) << 8; + ram->mr[0] |= (CL & 0x0f) << 3; + ram->mr[0] |= (WL & 0x07) << 0; + + ram->mr[1] &= ~0x0bf; + ram->mr[1] |= (xd & 0x01) << 7; + ram->mr[1] |= (at[0] & 0x03) << 4; + ram->mr[1] |= (dt & 0x03) << 2; + ram->mr[1] |= (ds & 0x03) << 0; + + /* this seems wrong, alternate field used for the broadcast + * on nuts vs non-nuts configs.. meh, it matches for now. + */ + ram->mr1_nuts = ram->mr[1]; + if (nuts) { + ram->mr[1] &= ~0x030; + ram->mr[1] |= (at[1] & 0x03) << 4; + } + + ram->mr[3] &= ~0x020; + ram->mr[3] |= (rq & 0x01) << 5; + + ram->mr[5] &= ~0x004; + ram->mr[5] |= (l3 << 2); + + if (!vo) + vo = (ram->mr[6] & 0xff0) >> 4; + if (ram->mr[6] & 0x001) + pd = 1; /* binary driver does this.. bug? */ + ram->mr[6] &= ~0xff1; + ram->mr[6] |= (vo & 0xff) << 4; + ram->mr[6] |= (pd & 0x01) << 0; + + if (NOTE00(vr)) { + ram->mr[7] &= ~0x300; + ram->mr[7] |= (vr & 0x03) << 8; + } + ram->mr[7] &= ~0x088; + ram->mr[7] |= (vh & 0x01) << 7; + ram->mr[7] |= (lf & 0x01) << 3; + + ram->mr[8] &= ~0x003; + ram->mr[8] |= (WR & 0x10) >> 3; + ram->mr[8] |= (CL & 0x10) >> 4; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c new file mode 100644 index 000000000..9dcc40f9e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c @@ -0,0 +1,147 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ram.h" + +#include +#include +#include + +void +gf100_fb_intr(struct nvkm_fb *base) +{ + struct gf100_fb *fb = gf100_fb(base); + struct nvkm_subdev *subdev = &fb->base.subdev; + struct nvkm_device *device = subdev->device; + u32 intr = nvkm_rd32(device, 0x000100); + if (intr & 0x08000000) + nvkm_debug(subdev, "PFFB intr\n"); + if (intr & 0x00002000) + nvkm_debug(subdev, "PBFB intr\n"); +} + +int +gf100_fb_oneinit(struct nvkm_fb *base) +{ + struct gf100_fb *fb = gf100_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + int ret, size = 1 << (fb->base.page ? fb->base.page : 17); + + size = nvkm_longopt(device->cfgopt, "MmuDebugBufferSize", size); + size = max(size, 0x1000); + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, size, 0x1000, + true, &fb->base.mmu_rd); + if (ret) + return ret; + + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, size, 0x1000, + true, &fb->base.mmu_wr); + if (ret) + return ret; + + fb->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (fb->r100c10_page) { + fb->r100c10 = dma_map_page(device->dev, fb->r100c10_page, 0, + PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(device->dev, fb->r100c10)) + return -EFAULT; + } + + return 0; +} + +int +gf100_fb_init_page(struct nvkm_fb *fb) +{ + struct nvkm_device *device = fb->subdev.device; + switch (fb->page) { + case 16: nvkm_mask(device, 0x100c80, 0x00000001, 0x00000001); break; + case 17: nvkm_mask(device, 0x100c80, 0x00000001, 0x00000000); break; + default: + return -EINVAL; + } + return 0; +} + +void +gf100_fb_init(struct nvkm_fb *base) +{ + struct gf100_fb *fb = gf100_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + + if (fb->r100c10_page) + nvkm_wr32(device, 0x100c10, fb->r100c10 >> 8); + + if (base->func->clkgate_pack) { + nvkm_therm_clkgate_init(device->therm, + base->func->clkgate_pack); + } +} + +void * +gf100_fb_dtor(struct nvkm_fb *base) +{ + struct gf100_fb *fb = gf100_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + + if (fb->r100c10_page) { + dma_unmap_page(device->dev, fb->r100c10, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(fb->r100c10_page); + } + + return fb; +} + +int +gf100_fb_new_(const struct nvkm_fb_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + struct gf100_fb *fb; + + if (!(fb = kzalloc(sizeof(*fb), GFP_KERNEL))) + return -ENOMEM; + nvkm_fb_ctor(func, device, type, inst, &fb->base); + *pfb = &fb->base; + + return 0; +} + +static const struct nvkm_fb_func +gf100_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gf100_fb_init, + .init_page = gf100_fb_init_page, + .intr = gf100_fb_intr, + .ram_new = gf100_ram_new, + .default_bigpage = 17, +}; + +int +gf100_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gf100_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h new file mode 100644 index 000000000..0cac7b06a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_RAM_NVC0_H__ +#define __NVKM_RAM_NVC0_H__ +#define gf100_fb(p) container_of((p), struct gf100_fb, base) +#include "priv.h" + +struct gf100_fb { + struct nvkm_fb base; + struct page *r100c10_page; + dma_addr_t r100c10; +}; + +int gf100_fb_new_(const struct nvkm_fb_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_fb **); +void *gf100_fb_dtor(struct nvkm_fb *); +void gf100_fb_init(struct nvkm_fb *); +void gf100_fb_intr(struct nvkm_fb *); + +void gp100_fb_init(struct nvkm_fb *); + +void gm200_fb_init(struct nvkm_fb *base); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c new file mode 100644 index 000000000..76678dd60 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ram.h" + +static const struct nvkm_fb_func +gf108_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gf100_fb_init, + .init_page = gf100_fb_init_page, + .intr = gf100_fb_intr, + .ram_new = gf108_ram_new, + .default_bigpage = 17, +}; + +int +gf108_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gf108_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c new file mode 100644 index 000000000..f73442ccb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c @@ -0,0 +1,89 @@ +/* + * 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 + * Lyude Paul + */ +#include "gk104.h" +#include "gf100.h" +#include "ram.h" + +/* + ******************************************************************************* + * PGRAPH registers for clockgating + ******************************************************************************* + */ +const struct nvkm_therm_clkgate_init +gk104_fb_clkgate_blcg_init_unk_0[] = { + { 0x100d10, 1, 0x0000c244 }, + { 0x100d30, 1, 0x0000c242 }, + { 0x100d3c, 1, 0x00000242 }, + { 0x100d48, 1, 0x00000242 }, + { 0x100d1c, 1, 0x00000042 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_fb_clkgate_blcg_init_vm_0[] = { + { 0x100c98, 1, 0x00000242 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_fb_clkgate_blcg_init_main_0[] = { + { 0x10f000, 1, 0x00000042 }, + { 0x17e030, 1, 0x00000044 }, + { 0x17e040, 1, 0x00000044 }, + {} +}; + +const struct nvkm_therm_clkgate_init +gk104_fb_clkgate_blcg_init_bcast_0[] = { + { 0x17ea60, 4, 0x00000044 }, + {} +}; + +static const struct nvkm_therm_clkgate_pack +gk104_fb_clkgate_pack[] = { + { gk104_fb_clkgate_blcg_init_unk_0 }, + { gk104_fb_clkgate_blcg_init_vm_0 }, + { gk104_fb_clkgate_blcg_init_main_0 }, + { gk104_fb_clkgate_blcg_init_bcast_0 }, + {} +}; + +static const struct nvkm_fb_func +gk104_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gf100_fb_init, + .init_page = gf100_fb_init_page, + .intr = gf100_fb_intr, + .ram_new = gk104_ram_new, + .default_bigpage = 17, + .clkgate_pack = gk104_fb_clkgate_pack, +}; + +int +gk104_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gk104_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.h new file mode 100644 index 000000000..b3c78e4ff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.h @@ -0,0 +1,35 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Lyude Paul + */ + +#ifndef __GK104_FB_H__ +#define __GK104_FB_H__ + +#include + +extern const struct nvkm_therm_clkgate_init gk104_fb_clkgate_blcg_init_unk_0[]; +extern const struct nvkm_therm_clkgate_init gk104_fb_clkgate_blcg_init_vm_0[]; +extern const struct nvkm_therm_clkgate_init gk104_fb_clkgate_blcg_init_main_0[]; +extern const struct nvkm_therm_clkgate_init gk104_fb_clkgate_blcg_init_bcast_0[]; + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk110.c new file mode 100644 index 000000000..45d6cdffa --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk110.c @@ -0,0 +1,71 @@ +/* + * Copyright 2017 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: Lyude Paul + */ +#include "gf100.h" +#include "gk104.h" +#include "ram.h" +#include +#include + +/* + ******************************************************************************* + * PGRAPH registers for clockgating + ******************************************************************************* + */ + +static const struct nvkm_therm_clkgate_init +gk110_fb_clkgate_blcg_init_unk_0[] = { + { 0x100d10, 1, 0x0000c242 }, + { 0x100d30, 1, 0x0000c242 }, + { 0x100d3c, 1, 0x00000242 }, + { 0x100d48, 1, 0x0000c242 }, + { 0x100d1c, 1, 0x00000042 }, + {} +}; + +static const struct nvkm_therm_clkgate_pack +gk110_fb_clkgate_pack[] = { + { gk110_fb_clkgate_blcg_init_unk_0 }, + { gk104_fb_clkgate_blcg_init_vm_0 }, + { gk104_fb_clkgate_blcg_init_main_0 }, + { gk104_fb_clkgate_blcg_init_bcast_0 }, + {} +}; + +static const struct nvkm_fb_func +gk110_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gf100_fb_init, + .init_page = gf100_fb_init_page, + .intr = gf100_fb_intr, + .ram_new = gk104_ram_new, + .default_bigpage = 17, + .clkgate_pack = gk110_fb_clkgate_pack, +}; + +int +gk110_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gk110_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c new file mode 100644 index 000000000..6bc42f89d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "gf100.h" + +/* GK20A's FB is similar to GF100's, but without the ability to allocate VRAM */ +static const struct nvkm_fb_func +gk20a_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gf100_fb_init, + .init_page = gf100_fb_init_page, + .intr = gf100_fb_intr, + .default_bigpage = 17, +}; + +int +gk20a_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gk20a_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c new file mode 100644 index 000000000..de52462a9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ram.h" + +static const struct nvkm_fb_func +gm107_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gf100_fb_init, + .init_page = gf100_fb_init_page, + .intr = gf100_fb_intr, + .ram_new = gm107_ram_new, + .default_bigpage = 17, +}; + +int +gm107_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gm107_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c new file mode 100644 index 000000000..5acf8d15d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c @@ -0,0 +1,73 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ram.h" + +#include + +int +gm200_fb_init_page(struct nvkm_fb *fb) +{ + struct nvkm_device *device = fb->subdev.device; + switch (fb->page) { + case 16: nvkm_mask(device, 0x100c80, 0x00001801, 0x00001001); break; + case 17: nvkm_mask(device, 0x100c80, 0x00001801, 0x00000000); break; + case 0: nvkm_mask(device, 0x100c80, 0x00001800, 0x00001800); break; + default: + return -EINVAL; + } + return 0; +} + +void +gm200_fb_init(struct nvkm_fb *base) +{ + struct gf100_fb *fb = gf100_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + + if (fb->r100c10_page) + nvkm_wr32(device, 0x100c10, fb->r100c10 >> 8); + + nvkm_wr32(device, 0x100cc8, nvkm_memory_addr(fb->base.mmu_wr) >> 8); + nvkm_wr32(device, 0x100ccc, nvkm_memory_addr(fb->base.mmu_rd) >> 8); + nvkm_mask(device, 0x100cc4, 0x00060000, + min(nvkm_memory_size(fb->base.mmu_rd) >> 16, (u64)2) << 17); +} + +static const struct nvkm_fb_func +gm200_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gm200_fb_init, + .init_page = gm200_fb_init_page, + .intr = gf100_fb_intr, + .ram_new = gm200_ram_new, + .default_bigpage = 0 /* per-instance. */, +}; + +int +gm200_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gm200_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm20b.c new file mode 100644 index 000000000..86f61a3f2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm20b.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "gf100.h" + +/* GM20B's FB is similar to GM200, but without the ability to allocate VRAM */ +static const struct nvkm_fb_func +gm20b_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gm200_fb_init, + .init_page = gm200_fb_init_page, + .intr = gf100_fb_intr, + .default_bigpage = 0 /* per-instance. */, +}; + +int +gm20b_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gm20b_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp100.c new file mode 100644 index 000000000..09e943edc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp100.c @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ram.h" + +#include + +void +gp100_fb_init_unkn(struct nvkm_fb *base) +{ + struct nvkm_device *device = gf100_fb(base)->base.subdev.device; + nvkm_wr32(device, 0x1fac80, nvkm_rd32(device, 0x100c80)); + nvkm_wr32(device, 0x1facc4, nvkm_rd32(device, 0x100cc4)); + nvkm_wr32(device, 0x1facc8, nvkm_rd32(device, 0x100cc8)); + nvkm_wr32(device, 0x1faccc, nvkm_rd32(device, 0x100ccc)); +} + +void +gp100_fb_init_remapper(struct nvkm_fb *fb) +{ + struct nvkm_device *device = fb->subdev.device; + /* Disable address remapper. */ + nvkm_mask(device, 0x100c14, 0x00040000, 0x00000000); +} + +void +gp100_fb_init(struct nvkm_fb *base) +{ + struct gf100_fb *fb = gf100_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + + if (fb->r100c10_page) + nvkm_wr32(device, 0x100c10, fb->r100c10 >> 8); + + nvkm_wr32(device, 0x100cc8, nvkm_memory_addr(fb->base.mmu_wr) >> 8); + nvkm_wr32(device, 0x100ccc, nvkm_memory_addr(fb->base.mmu_rd) >> 8); + nvkm_mask(device, 0x100cc4, 0x00060000, + min(nvkm_memory_size(fb->base.mmu_rd) >> 16, (u64)2) << 17); +} + +static const struct nvkm_fb_func +gp100_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gp100_fb_init, + .init_remapper = gp100_fb_init_remapper, + .init_page = gm200_fb_init_page, + .init_unkn = gp100_fb_init_unkn, + .ram_new = gp100_ram_new, +}; + +int +gp100_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gp100_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c new file mode 100644 index 000000000..0e78b3d73 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c @@ -0,0 +1,138 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "gf100.h" +#include "ram.h" + +#include +#include +#include +#include +#include + +int +gp102_fb_vpr_scrub(struct nvkm_fb *fb) +{ + struct nvkm_subdev *subdev = &fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_falcon *falcon = &device->nvdec[0]->falcon; + struct nvkm_blob *blob = &fb->vpr_scrubber; + const struct nvfw_bin_hdr *hsbin_hdr; + const struct nvfw_hs_header *fw_hdr; + const struct nvfw_hs_load_header *lhdr; + void *scrub_data; + u32 patch_loc, patch_sig; + int ret; + + nvkm_falcon_get(falcon, subdev); + + hsbin_hdr = nvfw_bin_hdr(subdev, blob->data); + fw_hdr = nvfw_hs_header(subdev, blob->data + hsbin_hdr->header_offset); + lhdr = nvfw_hs_load_header(subdev, blob->data + fw_hdr->hdr_offset); + scrub_data = blob->data + hsbin_hdr->data_offset; + + patch_loc = *(u32 *)(blob->data + fw_hdr->patch_loc); + patch_sig = *(u32 *)(blob->data + fw_hdr->patch_sig); + if (falcon->debug) { + memcpy(scrub_data + patch_loc, + blob->data + fw_hdr->sig_dbg_offset + patch_sig, + fw_hdr->sig_dbg_size); + } else { + memcpy(scrub_data + patch_loc, + blob->data + fw_hdr->sig_prod_offset + patch_sig, + fw_hdr->sig_prod_size); + } + + nvkm_falcon_reset(falcon); + nvkm_falcon_bind_context(falcon, NULL); + + nvkm_falcon_load_imem(falcon, scrub_data, lhdr->non_sec_code_off, + lhdr->non_sec_code_size, + lhdr->non_sec_code_off >> 8, 0, false); + nvkm_falcon_load_imem(falcon, scrub_data + lhdr->apps[0], + ALIGN(lhdr->apps[0], 0x100), + lhdr->apps[1], + lhdr->apps[0] >> 8, 0, true); + nvkm_falcon_load_dmem(falcon, scrub_data + lhdr->data_dma_base, 0, + lhdr->data_size, 0); + + nvkm_falcon_set_start_addr(falcon, 0x0); + nvkm_falcon_start(falcon); + + ret = nvkm_falcon_wait_for_halt(falcon, 500); + if (ret < 0) { + ret = -ETIMEDOUT; + goto end; + } + + /* put nvdec in clean state - without reset it will remain in HS mode */ + nvkm_falcon_reset(falcon); +end: + nvkm_falcon_put(falcon, subdev); + return ret; +} + +bool +gp102_fb_vpr_scrub_required(struct nvkm_fb *fb) +{ + struct nvkm_device *device = fb->subdev.device; + nvkm_wr32(device, 0x100cd0, 0x2); + return (nvkm_rd32(device, 0x100cd0) & 0x00000010) != 0; +} + +static const struct nvkm_fb_func +gp102_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gp100_fb_init, + .init_remapper = gp100_fb_init_remapper, + .init_page = gm200_fb_init_page, + .vpr.scrub_required = gp102_fb_vpr_scrub_required, + .vpr.scrub = gp102_fb_vpr_scrub, + .ram_new = gp100_ram_new, +}; + +int +gp102_fb_new_(const struct nvkm_fb_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + int ret = gf100_fb_new_(func, device, type, inst, pfb); + if (ret) + return ret; + + nvkm_firmware_load_blob(&(*pfb)->subdev, "nvdec/scrubber", "", 0, + &(*pfb)->vpr_scrubber); + return 0; +} + +int +gp102_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gp102_fb_new_(&gp102_fb, device, type, inst, pfb); +} + +MODULE_FIRMWARE("nvidia/gp102/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/gp104/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/gp106/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/gp107/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/gp108/nvdec/scrubber.bin"); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp10b.c new file mode 100644 index 000000000..84c9815a6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp10b.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" + +static const struct nvkm_fb_func +gp10b_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gm200_fb_init, + .init_page = gm200_fb_init_page, + .intr = gf100_fb_intr, +}; + +int +gp10b_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gf100_fb_new_(&gp10b_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c new file mode 100644 index 000000000..c1ec97586 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "ram.h" + +static const struct nv50_fb_func +gt215_fb = { + .ram_new = gt215_ram_new, + .tags = nv20_fb_tags, + .trap = 0x000d0fff, +}; + +int +gt215_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nv50_fb_new_(>215_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c new file mode 100644 index 000000000..63daa83ae --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gf100.h" +#include "ram.h" + +int +gv100_fb_init_page(struct nvkm_fb *fb) +{ + return (fb->page == 16) ? 0 : -EINVAL; +} + +static const struct nvkm_fb_func +gv100_fb = { + .dtor = gf100_fb_dtor, + .oneinit = gf100_fb_oneinit, + .init = gp100_fb_init, + .init_page = gv100_fb_init_page, + .init_unkn = gp100_fb_init_unkn, + .vpr.scrub_required = gp102_fb_vpr_scrub_required, + .vpr.scrub = gp102_fb_vpr_scrub, + .ram_new = gp100_ram_new, + .default_bigpage = 16, +}; + +int +gv100_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return gp102_fb_new_(&gv100_fb, device, type, inst, pfb); +} + +MODULE_FIRMWARE("nvidia/gv100/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/tu102/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/tu104/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/tu106/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/tu116/nvdec/scrubber.bin"); +MODULE_FIRMWARE("nvidia/tu117/nvdec/scrubber.bin"); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c new file mode 100644 index 000000000..70c7b08ee --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "ram.h" + +static const struct nv50_fb_func +mcp77_fb = { + .ram_new = mcp77_ram_new, + .trap = 0x001d07ff, +}; + +int +mcp77_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nv50_fb_new_(&mcp77_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c new file mode 100644 index 000000000..308d95516 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "ram.h" + +static const struct nv50_fb_func +mcp89_fb = { + .ram_new = mcp77_ram_new, + .trap = 0x089d1fff, +}; + +int +mcp89_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nv50_fb_new_(&mcp89_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c new file mode 100644 index 000000000..8d5a007ec --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "ram.h" +#include "regsnv04.h" + +static void +nv04_fb_init(struct nvkm_fb *fb) +{ + struct nvkm_device *device = fb->subdev.device; + + /* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows + * nvidia reading PFB_CFG_0, then writing back its original value. + * (which was 0x701114 in this case) + */ + nvkm_wr32(device, NV04_PFB_CFG0, 0x1114); +} + +static const struct nvkm_fb_func +nv04_fb = { + .init = nv04_fb_init, + .ram_new = nv04_ram_new, +}; + +int +nv04_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv04_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c new file mode 100644 index 000000000..7d2c16b27 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +void +nv10_fb_tile_init(struct nvkm_fb *fb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x80000000 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv10_fb_tile_fini(struct nvkm_fb *fb, int i, struct nvkm_fb_tile *tile) +{ + tile->addr = 0; + tile->limit = 0; + tile->pitch = 0; + tile->zcomp = 0; +} + +void +nv10_fb_tile_prog(struct nvkm_fb *fb, int i, struct nvkm_fb_tile *tile) +{ + struct nvkm_device *device = fb->subdev.device; + nvkm_wr32(device, 0x100244 + (i * 0x10), tile->limit); + nvkm_wr32(device, 0x100248 + (i * 0x10), tile->pitch); + nvkm_wr32(device, 0x100240 + (i * 0x10), tile->addr); + nvkm_rd32(device, 0x100240 + (i * 0x10)); +} + +static const struct nvkm_fb_func +nv10_fb = { + .tile.regions = 8, + .tile.init = nv10_fb_tile_init, + .tile.fini = nv10_fb_tile_fini, + .tile.prog = nv10_fb_tile_prog, + .ram_new = nv10_ram_new, +}; + +int +nv10_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv10_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c new file mode 100644 index 000000000..4bdad2abd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static const struct nvkm_fb_func +nv1a_fb = { + .tile.regions = 8, + .tile.init = nv10_fb_tile_init, + .tile.fini = nv10_fb_tile_fini, + .tile.prog = nv10_fb_tile_prog, + .ram_new = nv1a_ram_new, +}; + +int +nv1a_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv1a_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c new file mode 100644 index 000000000..d254f27f9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +void +nv20_fb_tile_init(struct nvkm_fb *fb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x00000001 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; + if (flags & 4) { + fb->func->tile.comp(fb, i, size, flags, tile); + tile->addr |= 2; + } +} + +static void +nv20_fb_tile_comp(struct nvkm_fb *fb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / fb->ram->parts, 0x40); + if (!nvkm_mm_head(&fb->tags.mm, 0, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */ + else tile->zcomp = 0x04000000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; + tile->zcomp |= 0x80000000; /* enable */ +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x08000000; +#endif + } +} + +void +nv20_fb_tile_fini(struct nvkm_fb *fb, int i, struct nvkm_fb_tile *tile) +{ + tile->addr = 0; + tile->limit = 0; + tile->pitch = 0; + tile->zcomp = 0; + nvkm_mm_free(&fb->tags.mm, &tile->tag); +} + +void +nv20_fb_tile_prog(struct nvkm_fb *fb, int i, struct nvkm_fb_tile *tile) +{ + struct nvkm_device *device = fb->subdev.device; + nvkm_wr32(device, 0x100244 + (i * 0x10), tile->limit); + nvkm_wr32(device, 0x100248 + (i * 0x10), tile->pitch); + nvkm_wr32(device, 0x100240 + (i * 0x10), tile->addr); + nvkm_rd32(device, 0x100240 + (i * 0x10)); + nvkm_wr32(device, 0x100300 + (i * 0x04), tile->zcomp); +} + +u32 +nv20_fb_tags(struct nvkm_fb *fb) +{ + const u32 tags = nvkm_rd32(fb->subdev.device, 0x100320); + return tags ? tags + 1 : 0; +} + +static const struct nvkm_fb_func +nv20_fb = { + .tags = nv20_fb_tags, + .tile.regions = 8, + .tile.init = nv20_fb_tile_init, + .tile.comp = nv20_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, + .ram_new = nv20_ram_new, +}; + +int +nv20_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv20_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c new file mode 100644 index 000000000..47da66dea --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static void +nv25_fb_tile_comp(struct nvkm_fb *fb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / fb->ram->parts, 0x40); + if (!nvkm_mm_head(&fb->tags.mm, 0, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */ + else tile->zcomp = 0x00200000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x01000000; +#endif + } +} + +static const struct nvkm_fb_func +nv25_fb = { + .tags = nv20_fb_tags, + .tile.regions = 8, + .tile.init = nv20_fb_tile_init, + .tile.comp = nv25_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, + .ram_new = nv20_ram_new, +}; + +int +nv25_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv25_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c new file mode 100644 index 000000000..0f87efb63 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +void +nv30_fb_tile_init(struct nvkm_fb *fb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) { + tile->addr = (0 << 4); + } else { + if (fb->func->tile.comp) /* z compression */ + fb->func->tile.comp(fb, i, size, flags, tile); + tile->addr = (1 << 4); + } + + tile->addr |= 0x00000001; /* enable */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +static void +nv30_fb_tile_comp(struct nvkm_fb *fb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / fb->ram->parts, 0x40); + if (!nvkm_mm_head(&fb->tags.mm, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */ + else tile->zcomp |= 0x02000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 12; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x10000000; +#endif + } +} + +static int +calc_bias(struct nvkm_fb *fb, int k, int i, int j) +{ + struct nvkm_device *device = fb->subdev.device; + int b = (device->chipset > 0x30 ? + nvkm_rd32(device, 0x122c + 0x10 * k + 0x4 * j) >> + (4 * (i ^ 1)) : + 0) & 0xf; + + return 2 * (b & 0x8 ? b - 0x10 : b); +} + +static int +calc_ref(struct nvkm_fb *fb, int l, int k, int i) +{ + int j, x = 0; + + for (j = 0; j < 4; j++) { + int m = (l >> (8 * i) & 0xff) + calc_bias(fb, k, i, j); + + x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j); + } + + return x; +} + +void +nv30_fb_init(struct nvkm_fb *fb) +{ + struct nvkm_device *device = fb->subdev.device; + int i, j; + + /* Init the memory timing regs at 0x10037c/0x1003ac */ + if (device->chipset == 0x30 || + device->chipset == 0x31 || + device->chipset == 0x35) { + /* Related to ROP count */ + int n = (device->chipset == 0x31 ? 2 : 4); + int l = nvkm_rd32(device, 0x1003d0); + + for (i = 0; i < n; i++) { + for (j = 0; j < 3; j++) + nvkm_wr32(device, 0x10037c + 0xc * i + 0x4 * j, + calc_ref(fb, l, 0, j)); + + for (j = 0; j < 2; j++) + nvkm_wr32(device, 0x1003ac + 0x8 * i + 0x4 * j, + calc_ref(fb, l, 1, j)); + } + } +} + +static const struct nvkm_fb_func +nv30_fb = { + .tags = nv20_fb_tags, + .init = nv30_fb_init, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv30_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, + .ram_new = nv20_ram_new, +}; + +int +nv30_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv30_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c new file mode 100644 index 000000000..0694dcfd1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static void +nv35_fb_tile_comp(struct nvkm_fb *fb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / fb->ram->parts, 0x40); + if (!nvkm_mm_head(&fb->tags.mm, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */ + else tile->zcomp |= 0x08000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } +} + +static const struct nvkm_fb_func +nv35_fb = { + .tags = nv20_fb_tags, + .init = nv30_fb_init, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv35_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, + .ram_new = nv20_ram_new, +}; + +int +nv35_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv35_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c new file mode 100644 index 000000000..1a3977037 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static void +nv36_fb_tile_comp(struct nvkm_fb *fb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / fb->ram->parts, 0x40); + if (!nvkm_mm_head(&fb->tags.mm, 0, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */ + else tile->zcomp |= 0x20000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 14; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x80000000; +#endif + } +} + +static const struct nvkm_fb_func +nv36_fb = { + .tags = nv20_fb_tags, + .init = nv30_fb_init, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv36_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, + .ram_new = nv20_ram_new, +}; + +int +nv36_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv36_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c new file mode 100644 index 000000000..77dbb9d6b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +void +nv40_fb_tile_comp(struct nvkm_fb *fb, int i, u32 size, u32 flags, + struct nvkm_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x80); + u32 tags = round_up(tiles / fb->ram->parts, 0x100); + if ( (flags & 2) && + !nvkm_mm_head(&fb->tags.mm, 0, 1, tags, tags, 1, &tile->tag)) { + tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */ + tile->zcomp |= ((tile->tag->offset ) >> 8); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } +} + +static void +nv40_fb_init(struct nvkm_fb *fb) +{ + nvkm_mask(fb->subdev.device, 0x10033c, 0x00008000, 0x00000000); +} + +static const struct nvkm_fb_func +nv40_fb = { + .tags = nv20_fb_tags, + .init = nv40_fb_init, + .tile.regions = 8, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv20_fb_tile_prog, + .ram_new = nv40_ram_new, +}; + +int +nv40_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv40_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c new file mode 100644 index 000000000..0f9d9e48e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +void +nv41_fb_tile_prog(struct nvkm_fb *fb, int i, struct nvkm_fb_tile *tile) +{ + struct nvkm_device *device = fb->subdev.device; + nvkm_wr32(device, 0x100604 + (i * 0x10), tile->limit); + nvkm_wr32(device, 0x100608 + (i * 0x10), tile->pitch); + nvkm_wr32(device, 0x100600 + (i * 0x10), tile->addr); + nvkm_rd32(device, 0x100600 + (i * 0x10)); + nvkm_wr32(device, 0x100700 + (i * 0x04), tile->zcomp); +} + +void +nv41_fb_init(struct nvkm_fb *fb) +{ + nvkm_wr32(fb->subdev.device, 0x100800, 0x00000001); +} + +static const struct nvkm_fb_func +nv41_fb = { + .tags = nv20_fb_tags, + .init = nv41_fb_init, + .tile.regions = 12, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, + .ram_new = nv41_ram_new, +}; + +int +nv41_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv41_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c new file mode 100644 index 000000000..b1046ee9f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static void +nv44_fb_tile_init(struct nvkm_fb *fb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + tile->addr = 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv44_fb_tile_prog(struct nvkm_fb *fb, int i, struct nvkm_fb_tile *tile) +{ + struct nvkm_device *device = fb->subdev.device; + nvkm_wr32(device, 0x100604 + (i * 0x10), tile->limit); + nvkm_wr32(device, 0x100608 + (i * 0x10), tile->pitch); + nvkm_wr32(device, 0x100600 + (i * 0x10), tile->addr); + nvkm_rd32(device, 0x100600 + (i * 0x10)); +} + +void +nv44_fb_init(struct nvkm_fb *fb) +{ + struct nvkm_device *device = fb->subdev.device; + nvkm_wr32(device, 0x100850, 0x80000000); + nvkm_wr32(device, 0x100800, 0x00000001); +} + +static const struct nvkm_fb_func +nv44_fb = { + .init = nv44_fb_init, + .tile.regions = 12, + .tile.init = nv44_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, + .ram_new = nv44_ram_new, +}; + +int +nv44_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv44_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c new file mode 100644 index 000000000..0d78de422 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +void +nv46_fb_tile_init(struct nvkm_fb *fb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nvkm_fb_tile *tile) +{ + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) tile->addr = (0 << 3); + else tile->addr = (1 << 3); + + tile->addr |= 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +static const struct nvkm_fb_func +nv46_fb = { + .init = nv44_fb_init, + .tile.regions = 15, + .tile.init = nv46_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, + .ram_new = nv44_ram_new, +}; + +int +nv46_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv46_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c new file mode 100644 index 000000000..5cedde29c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static const struct nvkm_fb_func +nv47_fb = { + .tags = nv20_fb_tags, + .init = nv41_fb_init, + .tile.regions = 15, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, + .ram_new = nv41_ram_new, +}; + +int +nv47_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv47_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c new file mode 100644 index 000000000..95cc09960 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static const struct nvkm_fb_func +nv49_fb = { + .tags = nv20_fb_tags, + .init = nv41_fb_init, + .tile.regions = 15, + .tile.init = nv30_fb_tile_init, + .tile.comp = nv40_fb_tile_comp, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv41_fb_tile_prog, + .ram_new = nv49_ram_new, +}; + +int +nv49_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv49_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c new file mode 100644 index 000000000..c9f3148f4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" +#include "ram.h" + +static const struct nvkm_fb_func +nv4e_fb = { + .init = nv44_fb_init, + .tile.regions = 12, + .tile.init = nv46_fb_tile_init, + .tile.fini = nv20_fb_tile_fini, + .tile.prog = nv44_fb_tile_prog, + .ram_new = nv44_ram_new, +}; + +int +nv4e_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nvkm_fb_new_(&nv4e_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c new file mode 100644 index 000000000..95fd8f834 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c @@ -0,0 +1,288 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "nv50.h" +#include "ram.h" + +#include +#include +#include + +static int +nv50_fb_ram_new(struct nvkm_fb *base, struct nvkm_ram **pram) +{ + struct nv50_fb *fb = nv50_fb(base); + return fb->func->ram_new(&fb->base, pram); +} + +static const struct nvkm_enum vm_dispatch_subclients[] = { + { 0x00000000, "GRCTX" }, + { 0x00000001, "NOTIFY" }, + { 0x00000002, "QUERY" }, + { 0x00000003, "COND" }, + { 0x00000004, "M2M_IN" }, + { 0x00000005, "M2M_OUT" }, + { 0x00000006, "M2M_NOTIFY" }, + {} +}; + +static const struct nvkm_enum vm_ccache_subclients[] = { + { 0x00000000, "CB" }, + { 0x00000001, "TIC" }, + { 0x00000002, "TSC" }, + {} +}; + +static const struct nvkm_enum vm_prop_subclients[] = { + { 0x00000000, "RT0" }, + { 0x00000001, "RT1" }, + { 0x00000002, "RT2" }, + { 0x00000003, "RT3" }, + { 0x00000004, "RT4" }, + { 0x00000005, "RT5" }, + { 0x00000006, "RT6" }, + { 0x00000007, "RT7" }, + { 0x00000008, "ZETA" }, + { 0x00000009, "LOCAL" }, + { 0x0000000a, "GLOBAL" }, + { 0x0000000b, "STACK" }, + { 0x0000000c, "DST2D" }, + {} +}; + +static const struct nvkm_enum vm_pfifo_subclients[] = { + { 0x00000000, "PUSHBUF" }, + { 0x00000001, "SEMAPHORE" }, + {} +}; + +static const struct nvkm_enum vm_bar_subclients[] = { + { 0x00000000, "FB" }, + { 0x00000001, "IN" }, + {} +}; + +static const struct nvkm_enum vm_client[] = { + { 0x00000000, "STRMOUT" }, + { 0x00000003, "DISPATCH", vm_dispatch_subclients }, + { 0x00000004, "PFIFO_WRITE" }, + { 0x00000005, "CCACHE", vm_ccache_subclients }, + { 0x00000006, "PMSPPP" }, + { 0x00000007, "CLIPID" }, + { 0x00000008, "PFIFO_READ" }, + { 0x00000009, "VFETCH" }, + { 0x0000000a, "TEXTURE" }, + { 0x0000000b, "PROP", vm_prop_subclients }, + { 0x0000000c, "PVP" }, + { 0x0000000d, "PBSP" }, + { 0x0000000e, "PCRYPT" }, + { 0x0000000f, "PCOUNTER" }, + { 0x00000011, "PDAEMON" }, + {} +}; + +static const struct nvkm_enum vm_engine[] = { + { 0x00000000, "PGRAPH" }, + { 0x00000001, "PVP" }, + { 0x00000004, "PEEPHOLE" }, + { 0x00000005, "PFIFO", vm_pfifo_subclients }, + { 0x00000006, "BAR", vm_bar_subclients }, + { 0x00000008, "PMSPPP" }, + { 0x00000008, "PMPEG" }, + { 0x00000009, "PBSP" }, + { 0x0000000a, "PCRYPT" }, + { 0x0000000b, "PCOUNTER" }, + { 0x0000000c, "SEMAPHORE_BG" }, + { 0x0000000d, "PCE0" }, + { 0x0000000e, "PMU" }, + {} +}; + +static const struct nvkm_enum vm_fault[] = { + { 0x00000000, "PT_NOT_PRESENT" }, + { 0x00000001, "PT_TOO_SHORT" }, + { 0x00000002, "PAGE_NOT_PRESENT" }, + { 0x00000003, "PAGE_SYSTEM_ONLY" }, + { 0x00000004, "PAGE_READ_ONLY" }, + { 0x00000006, "NULL_DMAOBJ" }, + { 0x00000007, "WRONG_MEMTYPE" }, + { 0x0000000b, "VRAM_LIMIT" }, + { 0x0000000f, "DMAOBJ_LIMIT" }, + {} +}; + +static void +nv50_fb_intr(struct nvkm_fb *base) +{ + struct nv50_fb *fb = nv50_fb(base); + struct nvkm_subdev *subdev = &fb->base.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_fifo *fifo = device->fifo; + struct nvkm_fifo_chan *chan; + const struct nvkm_enum *en, *re, *cl, *sc; + u32 trap[6], idx, inst; + u8 st0, st1, st2, st3; + unsigned long flags; + int i; + + idx = nvkm_rd32(device, 0x100c90); + if (!(idx & 0x80000000)) + return; + idx &= 0x00ffffff; + + for (i = 0; i < 6; i++) { + nvkm_wr32(device, 0x100c90, idx | i << 24); + trap[i] = nvkm_rd32(device, 0x100c94); + } + nvkm_wr32(device, 0x100c90, idx | 0x80000000); + + /* decode status bits into something more useful */ + if (device->chipset < 0xa3 || + device->chipset == 0xaa || device->chipset == 0xac) { + st0 = (trap[0] & 0x0000000f) >> 0; + st1 = (trap[0] & 0x000000f0) >> 4; + st2 = (trap[0] & 0x00000f00) >> 8; + st3 = (trap[0] & 0x0000f000) >> 12; + } else { + st0 = (trap[0] & 0x000000ff) >> 0; + st1 = (trap[0] & 0x0000ff00) >> 8; + st2 = (trap[0] & 0x00ff0000) >> 16; + st3 = (trap[0] & 0xff000000) >> 24; + } + inst = ((trap[2] << 16) | trap[1]) << 12; + + en = nvkm_enum_find(vm_engine, st0); + re = nvkm_enum_find(vm_fault , st1); + cl = nvkm_enum_find(vm_client, st2); + if (cl && cl->data) sc = nvkm_enum_find(cl->data, st3); + else if (en && en->data) sc = nvkm_enum_find(en->data, st3); + else sc = NULL; + + chan = nvkm_fifo_chan_inst(fifo, inst, &flags); + nvkm_error(subdev, "trapped %s at %02x%04x%04x on channel %d [%08x %s] " + "engine %02x [%s] client %02x [%s] " + "subclient %02x [%s] reason %08x [%s]\n", + (trap[5] & 0x00000100) ? "read" : "write", + trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, + chan ? chan->chid : -1, inst, + chan ? chan->object.client->name : "unknown", + st0, en ? en->name : "", + st2, cl ? cl->name : "", st3, sc ? sc->name : "", + st1, re ? re->name : ""); + nvkm_fifo_chan_put(fifo, flags, &chan); +} + +static int +nv50_fb_oneinit(struct nvkm_fb *base) +{ + struct nv50_fb *fb = nv50_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + + fb->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (fb->r100c08_page) { + fb->r100c08 = dma_map_page(device->dev, fb->r100c08_page, 0, + PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(device->dev, fb->r100c08)) + return -EFAULT; + } + + return 0; +} + +static void +nv50_fb_init(struct nvkm_fb *base) +{ + struct nv50_fb *fb = nv50_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + + /* Not a clue what this is exactly. Without pointing it at a + * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) + * cause IOMMU "read from address 0" errors (rh#561267) + */ + nvkm_wr32(device, 0x100c08, fb->r100c08 >> 8); + + /* This is needed to get meaningful information from 100c90 + * on traps. No idea what these values mean exactly. */ + nvkm_wr32(device, 0x100c90, fb->func->trap); +} + +static u32 +nv50_fb_tags(struct nvkm_fb *base) +{ + struct nv50_fb *fb = nv50_fb(base); + if (fb->func->tags) + return fb->func->tags(&fb->base); + return 0; +} + +static void * +nv50_fb_dtor(struct nvkm_fb *base) +{ + struct nv50_fb *fb = nv50_fb(base); + struct nvkm_device *device = fb->base.subdev.device; + + if (fb->r100c08_page) { + dma_unmap_page(device->dev, fb->r100c08, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(fb->r100c08_page); + } + + return fb; +} + +static const struct nvkm_fb_func +nv50_fb_ = { + .dtor = nv50_fb_dtor, + .tags = nv50_fb_tags, + .oneinit = nv50_fb_oneinit, + .init = nv50_fb_init, + .intr = nv50_fb_intr, + .ram_new = nv50_fb_ram_new, +}; + +int +nv50_fb_new_(const struct nv50_fb_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + struct nv50_fb *fb; + + if (!(fb = kzalloc(sizeof(*fb), GFP_KERNEL))) + return -ENOMEM; + nvkm_fb_ctor(&nv50_fb_, device, type, inst, &fb->base); + fb->func = func; + *pfb = &fb->base; + return 0; +} + +static const struct nv50_fb_func +nv50_fb = { + .ram_new = nv50_ram_new, + .tags = nv20_fb_tags, + .trap = 0x000707ff, +}; + +int +nv50_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb) +{ + return nv50_fb_new_(&nv50_fb, device, type, inst, pfb); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h new file mode 100644 index 000000000..a5e673859 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FB_NV50_H__ +#define __NVKM_FB_NV50_H__ +#define nv50_fb(p) container_of((p), struct nv50_fb, base) +#include "priv.h" + +struct nv50_fb { + const struct nv50_fb_func *func; + struct nvkm_fb base; + struct page *r100c08_page; + dma_addr_t r100c08; +}; + +struct nv50_fb_func { + int (*ram_new)(struct nvkm_fb *, struct nvkm_ram **); + u32 (*tags)(struct nvkm_fb *); + u32 trap; +}; + +int nv50_fb_new_(const struct nv50_fb_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_fb **pfb); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h new file mode 100644 index 000000000..3f1be9780 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FB_PRIV_H__ +#define __NVKM_FB_PRIV_H__ +#define nvkm_fb(p) container_of((p), struct nvkm_fb, subdev) +#include +#include +struct nvkm_bios; + +struct nvkm_fb_func { + void *(*dtor)(struct nvkm_fb *); + u32 (*tags)(struct nvkm_fb *); + int (*oneinit)(struct nvkm_fb *); + void (*init)(struct nvkm_fb *); + void (*init_remapper)(struct nvkm_fb *); + int (*init_page)(struct nvkm_fb *); + void (*init_unkn)(struct nvkm_fb *); + void (*intr)(struct nvkm_fb *); + + struct { + bool (*scrub_required)(struct nvkm_fb *); + int (*scrub)(struct nvkm_fb *); + } vpr; + + struct { + int regions; + void (*init)(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); + void (*comp)(struct nvkm_fb *, int i, u32 size, u32 flags, + struct nvkm_fb_tile *); + void (*fini)(struct nvkm_fb *, int i, struct nvkm_fb_tile *); + void (*prog)(struct nvkm_fb *, int i, struct nvkm_fb_tile *); + } tile; + + int (*ram_new)(struct nvkm_fb *, struct nvkm_ram **); + + u8 default_bigpage; + const struct nvkm_therm_clkgate_pack *clkgate_pack; +}; + +void nvkm_fb_ctor(const struct nvkm_fb_func *, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fb *); +int nvkm_fb_new_(const struct nvkm_fb_func *, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fb **); +int nvkm_fb_bios_memtype(struct nvkm_bios *); + +void nv10_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +void nv10_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *); +void nv10_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +u32 nv20_fb_tags(struct nvkm_fb *); +void nv20_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); +void nv20_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *); +void nv20_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +void nv30_fb_init(struct nvkm_fb *); +void nv30_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); + +void nv40_fb_tile_comp(struct nvkm_fb *, int i, u32 size, u32 flags, + struct nvkm_fb_tile *); + +void nv41_fb_init(struct nvkm_fb *); +void nv41_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +void nv44_fb_init(struct nvkm_fb *); +void nv44_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *); + +void nv46_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nvkm_fb_tile *); + +int gf100_fb_oneinit(struct nvkm_fb *); +int gf100_fb_init_page(struct nvkm_fb *); + +int gm200_fb_init_page(struct nvkm_fb *); + +void gp100_fb_init_remapper(struct nvkm_fb *); +void gp100_fb_init_unkn(struct nvkm_fb *); + +int gp102_fb_new_(const struct nvkm_fb_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_fb **); +bool gp102_fb_vpr_scrub_required(struct nvkm_fb *); +int gp102_fb_vpr_scrub(struct nvkm_fb *); + +int gv100_fb_init_page(struct nvkm_fb *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.c new file mode 100644 index 000000000..03b1bdb27 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.c @@ -0,0 +1,219 @@ +/* + * Copyright 2015 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 + */ +#define nvkm_vram(p) container_of((p), struct nvkm_vram, memory) +#include "ram.h" + +#include +#include + +struct nvkm_vram { + struct nvkm_memory memory; + struct nvkm_ram *ram; + u8 page; + struct nvkm_mm_node *mn; +}; + +static int +nvkm_vram_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm, + struct nvkm_vma *vma, void *argv, u32 argc) +{ + struct nvkm_vram *vram = nvkm_vram(memory); + struct nvkm_vmm_map map = { + .memory = &vram->memory, + .offset = offset, + .mem = vram->mn, + }; + + return nvkm_vmm_map(vmm, vma, argv, argc, &map); +} + +static u64 +nvkm_vram_size(struct nvkm_memory *memory) +{ + return (u64)nvkm_mm_size(nvkm_vram(memory)->mn) << NVKM_RAM_MM_SHIFT; +} + +static u64 +nvkm_vram_addr(struct nvkm_memory *memory) +{ + struct nvkm_vram *vram = nvkm_vram(memory); + if (!nvkm_mm_contiguous(vram->mn)) + return ~0ULL; + return (u64)nvkm_mm_addr(vram->mn) << NVKM_RAM_MM_SHIFT; +} + +static u8 +nvkm_vram_page(struct nvkm_memory *memory) +{ + return nvkm_vram(memory)->page; +} + +static enum nvkm_memory_target +nvkm_vram_target(struct nvkm_memory *memory) +{ + return NVKM_MEM_TARGET_VRAM; +} + +static void * +nvkm_vram_dtor(struct nvkm_memory *memory) +{ + struct nvkm_vram *vram = nvkm_vram(memory); + struct nvkm_mm_node *next = vram->mn; + struct nvkm_mm_node *node; + mutex_lock(&vram->ram->mutex); + while ((node = next)) { + next = node->next; + nvkm_mm_free(&vram->ram->vram, &node); + } + mutex_unlock(&vram->ram->mutex); + return vram; +} + +static const struct nvkm_memory_func +nvkm_vram = { + .dtor = nvkm_vram_dtor, + .target = nvkm_vram_target, + .page = nvkm_vram_page, + .addr = nvkm_vram_addr, + .size = nvkm_vram_size, + .map = nvkm_vram_map, +}; + +int +nvkm_ram_get(struct nvkm_device *device, u8 heap, u8 type, u8 rpage, u64 size, + bool contig, bool back, struct nvkm_memory **pmemory) +{ + struct nvkm_ram *ram; + struct nvkm_mm *mm; + struct nvkm_mm_node **node, *r; + struct nvkm_vram *vram; + u8 page = max(rpage, (u8)NVKM_RAM_MM_SHIFT); + u32 align = (1 << page) >> NVKM_RAM_MM_SHIFT; + u32 max = ALIGN(size, 1 << page) >> NVKM_RAM_MM_SHIFT; + u32 min = contig ? max : align; + int ret; + + if (!device->fb || !(ram = device->fb->ram)) + return -ENODEV; + ram = device->fb->ram; + mm = &ram->vram; + + if (!(vram = kzalloc(sizeof(*vram), GFP_KERNEL))) + return -ENOMEM; + nvkm_memory_ctor(&nvkm_vram, &vram->memory); + vram->ram = ram; + vram->page = page; + *pmemory = &vram->memory; + + mutex_lock(&ram->mutex); + node = &vram->mn; + do { + if (back) + ret = nvkm_mm_tail(mm, heap, type, max, min, align, &r); + else + ret = nvkm_mm_head(mm, heap, type, max, min, align, &r); + if (ret) { + mutex_unlock(&ram->mutex); + nvkm_memory_unref(pmemory); + return ret; + } + + *node = r; + node = &r->next; + max -= r->length; + } while (max); + mutex_unlock(&ram->mutex); + return 0; +} + +int +nvkm_ram_init(struct nvkm_ram *ram) +{ + if (ram->func->init) + return ram->func->init(ram); + return 0; +} + +void +nvkm_ram_del(struct nvkm_ram **pram) +{ + struct nvkm_ram *ram = *pram; + if (ram && !WARN_ON(!ram->func)) { + if (ram->func->dtor) + *pram = ram->func->dtor(ram); + nvkm_mm_fini(&ram->vram); + mutex_destroy(&ram->mutex); + kfree(*pram); + *pram = NULL; + } +} + +int +nvkm_ram_ctor(const struct nvkm_ram_func *func, struct nvkm_fb *fb, + enum nvkm_ram_type type, u64 size, struct nvkm_ram *ram) +{ + static const char *name[] = { + [NVKM_RAM_TYPE_UNKNOWN] = "of unknown memory type", + [NVKM_RAM_TYPE_STOLEN ] = "stolen system memory", + [NVKM_RAM_TYPE_SGRAM ] = "SGRAM", + [NVKM_RAM_TYPE_SDRAM ] = "SDRAM", + [NVKM_RAM_TYPE_DDR1 ] = "DDR1", + [NVKM_RAM_TYPE_DDR2 ] = "DDR2", + [NVKM_RAM_TYPE_DDR3 ] = "DDR3", + [NVKM_RAM_TYPE_GDDR2 ] = "GDDR2", + [NVKM_RAM_TYPE_GDDR3 ] = "GDDR3", + [NVKM_RAM_TYPE_GDDR4 ] = "GDDR4", + [NVKM_RAM_TYPE_GDDR5 ] = "GDDR5", + [NVKM_RAM_TYPE_GDDR5X ] = "GDDR5X", + [NVKM_RAM_TYPE_GDDR6 ] = "GDDR6", + [NVKM_RAM_TYPE_HBM2 ] = "HBM2", + }; + struct nvkm_subdev *subdev = &fb->subdev; + int ret; + + nvkm_info(subdev, "%d MiB %s\n", (int)(size >> 20), name[type]); + ram->func = func; + ram->fb = fb; + ram->type = type; + ram->size = size; + mutex_init(&ram->mutex); + + if (!nvkm_mm_initialised(&ram->vram)) { + ret = nvkm_mm_init(&ram->vram, NVKM_RAM_MM_NORMAL, 0, + size >> NVKM_RAM_MM_SHIFT, 1); + if (ret) + return ret; + } + + return 0; +} + +int +nvkm_ram_new_(const struct nvkm_ram_func *func, struct nvkm_fb *fb, + enum nvkm_ram_type type, u64 size, struct nvkm_ram **pram) +{ + if (!(*pram = kzalloc(sizeof(**pram), GFP_KERNEL))) + return -ENOMEM; + return nvkm_ram_ctor(func, fb, type, size, *pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h new file mode 100644 index 000000000..ea7d66f3d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FB_RAM_PRIV_H__ +#define __NVKM_FB_RAM_PRIV_H__ +#include "priv.h" + +int nvkm_ram_ctor(const struct nvkm_ram_func *, struct nvkm_fb *, + enum nvkm_ram_type, u64 size, struct nvkm_ram *); +int nvkm_ram_new_(const struct nvkm_ram_func *, struct nvkm_fb *, + enum nvkm_ram_type, u64 size, struct nvkm_ram **); +void nvkm_ram_del(struct nvkm_ram **); +int nvkm_ram_init(struct nvkm_ram *); + +extern const struct nvkm_ram_func nv04_ram_func; + +int nv50_ram_ctor(const struct nvkm_ram_func *, struct nvkm_fb *, + struct nvkm_ram *); + +int gf100_ram_new_(const struct nvkm_ram_func *, struct nvkm_fb *, + struct nvkm_ram **); +int gf100_ram_ctor(const struct nvkm_ram_func *, struct nvkm_fb *, + struct nvkm_ram *); +u32 gf100_ram_probe_fbp(const struct nvkm_ram_func *, + struct nvkm_device *, int, int *); +u32 gf100_ram_probe_fbp_amount(const struct nvkm_ram_func *, u32, + struct nvkm_device *, int, int *); +u32 gf100_ram_probe_fbpa_amount(struct nvkm_device *, int); +int gf100_ram_init(struct nvkm_ram *); +int gf100_ram_calc(struct nvkm_ram *, u32); +int gf100_ram_prog(struct nvkm_ram *); +void gf100_ram_tidy(struct nvkm_ram *); + +u32 gf108_ram_probe_fbp_amount(const struct nvkm_ram_func *, u32, + struct nvkm_device *, int, int *); + +int gk104_ram_new_(const struct nvkm_ram_func *, struct nvkm_fb *, + struct nvkm_ram **); +void *gk104_ram_dtor(struct nvkm_ram *); +int gk104_ram_init(struct nvkm_ram *); +int gk104_ram_calc(struct nvkm_ram *, u32); +int gk104_ram_prog(struct nvkm_ram *); +void gk104_ram_tidy(struct nvkm_ram *); + +u32 gm107_ram_probe_fbp(const struct nvkm_ram_func *, + struct nvkm_device *, int, int *); + +u32 gm200_ram_probe_fbp_amount(const struct nvkm_ram_func *, u32, + struct nvkm_device *, int, int *); + +/* RAM type-specific MR calculation routines */ +int nvkm_sddr2_calc(struct nvkm_ram *); +int nvkm_sddr3_calc(struct nvkm_ram *); +int nvkm_gddr3_calc(struct nvkm_ram *); +int nvkm_gddr5_calc(struct nvkm_ram *, bool nuts); + +int nv04_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv10_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv1a_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv20_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv40_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv41_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv44_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv49_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv4e_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int nv50_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gt215_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int mcp77_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gf100_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gf108_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gk104_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gm107_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gm200_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gp100_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int ga102_ram_new(struct nvkm_fb *, struct nvkm_ram **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h new file mode 100644 index 000000000..247c0f8a7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FBRAM_FUC_H__ +#define __NVKM_FBRAM_FUC_H__ +#include +#include + +struct ramfuc { + struct nvkm_memx *memx; + struct nvkm_fb *fb; + int sequence; +}; + +struct ramfuc_reg { + int sequence; + bool force; + u32 addr; + u32 stride; /* in bytes */ + u32 mask; + u32 data; +}; + +static inline struct ramfuc_reg +ramfuc_stride(u32 addr, u32 stride, u32 mask) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = stride, + .mask = mask, + .data = 0xdeadbeef, + }; +} + +static inline struct ramfuc_reg +ramfuc_reg2(u32 addr1, u32 addr2) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr1, + .stride = addr2 - addr1, + .mask = 0x3, + .data = 0xdeadbeef, + }; +} + +static noinline struct ramfuc_reg +ramfuc_reg(u32 addr) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = 0, + .mask = 0x1, + .data = 0xdeadbeef, + }; +} + +static inline int +ramfuc_init(struct ramfuc *ram, struct nvkm_fb *fb) +{ + int ret = nvkm_memx_init(fb->subdev.device->pmu, &ram->memx); + if (ret) + return ret; + + ram->sequence++; + ram->fb = fb; + return 0; +} + +static inline int +ramfuc_exec(struct ramfuc *ram, bool exec) +{ + int ret = 0; + if (ram->fb) { + ret = nvkm_memx_fini(&ram->memx, exec); + ram->fb = NULL; + } + return ret; +} + +static inline u32 +ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg) +{ + struct nvkm_device *device = ram->fb->subdev.device; + if (reg->sequence != ram->sequence) + reg->data = nvkm_rd32(device, reg->addr); + return reg->data; +} + +static inline void +ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data) +{ + unsigned int mask, off = 0; + + reg->sequence = ram->sequence; + reg->data = data; + + for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) { + if (mask & 1) + nvkm_memx_wr32(ram->memx, reg->addr+off, reg->data); + off += reg->stride; + } +} + +static inline void +ramfuc_nuke(struct ramfuc *ram, struct ramfuc_reg *reg) +{ + reg->force = true; +} + +static inline u32 +ramfuc_mask(struct ramfuc *ram, struct ramfuc_reg *reg, u32 mask, u32 data) +{ + u32 temp = ramfuc_rd32(ram, reg); + if (temp != ((temp & ~mask) | data) || reg->force) { + ramfuc_wr32(ram, reg, (temp & ~mask) | data); + reg->force = false; + } + return temp; +} + +static inline void +ramfuc_wait(struct ramfuc *ram, u32 addr, u32 mask, u32 data, u32 nsec) +{ + nvkm_memx_wait(ram->memx, addr, mask, data, nsec); +} + +static inline void +ramfuc_nsec(struct ramfuc *ram, u32 nsec) +{ + nvkm_memx_nsec(ram->memx, nsec); +} + +static inline void +ramfuc_wait_vblank(struct ramfuc *ram) +{ + nvkm_memx_wait_vblank(ram->memx); +} + +static inline void +ramfuc_train(struct ramfuc *ram) +{ + nvkm_memx_train(ram->memx); +} + +static inline int +ramfuc_train_result(struct nvkm_fb *fb, u32 *result, u32 rsize) +{ + return nvkm_memx_train_result(fb->subdev.device->pmu, result, rsize); +} + +static inline void +ramfuc_block(struct ramfuc *ram) +{ + nvkm_memx_block(ram->memx); +} + +static inline void +ramfuc_unblock(struct ramfuc *ram) +{ + nvkm_memx_unblock(ram->memx); +} + +#define ram_init(s,p) ramfuc_init(&(s)->base, (p)) +#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e)) +#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r) +#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d)) +#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r) +#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n)) +#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n)) +#define ram_wait_vblank(s) ramfuc_wait_vblank(&(s)->base) +#define ram_train(s) ramfuc_train(&(s)->base) +#define ram_train_result(s,r,l) ramfuc_train_result((s), (r), (l)) +#define ram_block(s) ramfuc_block(&(s)->base) +#define ram_unblock(s) ramfuc_unblock(&(s)->base) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c new file mode 100644 index 000000000..298c136ce --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ram.h" + +#include +#include +#include + +static const struct nvkm_ram_func +ga102_ram = { +}; + +int +ga102_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + enum nvkm_ram_type type = nvkm_fb_bios_memtype(device->bios); + u32 size = nvkm_rd32(device, 0x1183a4); + + return nvkm_ram_new_(&ga102_ram, fb, type, (u64)size << 20, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c new file mode 100644 index 000000000..ba43fe158 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c @@ -0,0 +1,672 @@ +/* + * 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 + */ +#define gf100_ram(p) container_of((p), struct gf100_ram, base) +#include "ram.h" +#include "ramfuc.h" + +#include +#include +#include +#include +#include +#include +#include + +struct gf100_ramfuc { + struct ramfuc base; + + struct ramfuc_reg r_0x10fe20; + struct ramfuc_reg r_0x10fe24; + struct ramfuc_reg r_0x137320; + struct ramfuc_reg r_0x137330; + + struct ramfuc_reg r_0x132000; + struct ramfuc_reg r_0x132004; + struct ramfuc_reg r_0x132100; + + struct ramfuc_reg r_0x137390; + + struct ramfuc_reg r_0x10f290; + struct ramfuc_reg r_0x10f294; + struct ramfuc_reg r_0x10f298; + struct ramfuc_reg r_0x10f29c; + struct ramfuc_reg r_0x10f2a0; + + struct ramfuc_reg r_0x10f300; + struct ramfuc_reg r_0x10f338; + struct ramfuc_reg r_0x10f340; + struct ramfuc_reg r_0x10f344; + struct ramfuc_reg r_0x10f348; + + struct ramfuc_reg r_0x10f910; + struct ramfuc_reg r_0x10f914; + + struct ramfuc_reg r_0x100b0c; + struct ramfuc_reg r_0x10f050; + struct ramfuc_reg r_0x10f090; + struct ramfuc_reg r_0x10f200; + struct ramfuc_reg r_0x10f210; + struct ramfuc_reg r_0x10f310; + struct ramfuc_reg r_0x10f314; + struct ramfuc_reg r_0x10f610; + struct ramfuc_reg r_0x10f614; + struct ramfuc_reg r_0x10f800; + struct ramfuc_reg r_0x10f808; + struct ramfuc_reg r_0x10f824; + struct ramfuc_reg r_0x10f830; + struct ramfuc_reg r_0x10f988; + struct ramfuc_reg r_0x10f98c; + struct ramfuc_reg r_0x10f990; + struct ramfuc_reg r_0x10f998; + struct ramfuc_reg r_0x10f9b0; + struct ramfuc_reg r_0x10f9b4; + struct ramfuc_reg r_0x10fb04; + struct ramfuc_reg r_0x10fb08; + struct ramfuc_reg r_0x137300; + struct ramfuc_reg r_0x137310; + struct ramfuc_reg r_0x137360; + struct ramfuc_reg r_0x1373ec; + struct ramfuc_reg r_0x1373f0; + struct ramfuc_reg r_0x1373f8; + + struct ramfuc_reg r_0x61c140; + struct ramfuc_reg r_0x611200; + + struct ramfuc_reg r_0x13d8f4; +}; + +struct gf100_ram { + struct nvkm_ram base; + struct gf100_ramfuc fuc; + struct nvbios_pll refpll; + struct nvbios_pll mempll; +}; + +static void +gf100_ram_train(struct gf100_ramfuc *fuc, u32 magic) +{ + struct gf100_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nvkm_fb *fb = ram->base.fb; + struct nvkm_device *device = fb->subdev.device; + u32 part = nvkm_rd32(device, 0x022438), i; + u32 mask = nvkm_rd32(device, 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); + } +} + +int +gf100_ram_calc(struct nvkm_ram *base, u32 freq) +{ + struct gf100_ram *ram = gf100_ram(base); + struct gf100_ramfuc *fuc = &ram->fuc; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_clk *clk = device->clk; + struct nvkm_bios *bios = device->bios; + struct nvbios_ramcfg cfg; + u8 ver, cnt, len, strap; + struct { + u32 data; + u8 size; + } rammap, ramcfg, timing; + int ref, div, out; + int from, mode; + int N1, M1, P; + int ret; + + /* lookup memory config data relevant to the target frequency */ + rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size, + &cnt, &ramcfg.size, &cfg); + if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) { + nvkm_error(subdev, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(subdev); + if (strap >= cnt) { + nvkm_error(subdev, "invalid ramcfg strap\n"); + return -EINVAL; + } + + ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size); + if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) { + nvkm_error(subdev, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + strap = nvbios_rd08(bios, ramcfg.data + 0x01); + if (strap != 0xff) { + timing.data = nvbios_timingEe(bios, strap, &ver, &timing.size, + &cnt, &len); + if (!timing.data || ver != 0x10 || timing.size < 0x19) { + nvkm_error(subdev, "invalid/missing timing entry\n"); + return -EINVAL; + } + } else { + timing.data = 0; + } + + ret = ram_init(fuc, ram->base.fb); + 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 = nvkm_clk_read(clk, nv_clk_src_sppll0); + else + ref = nvkm_clk_read(clk, nv_clk_src_sppll1); + div = max(min((ref * 2) / freq, (u32)65), (u32)2) - 2; + out = (ref * 2) / (div + 2); + mode = freq != out; + + ram_mask(fuc, 0x137360, 0x00000002, 0x00000000); + + if ((ram_rd32(fuc, 0x132000) & 0x00000002) || 0 /*XXX*/) { + ram_nuke(fuc, 0x132000); + ram_mask(fuc, 0x132000, 0x00000002, 0x00000002); + ram_mask(fuc, 0x132000, 0x00000002, 0x00000000); + } + + if (mode == 1) { + ram_nuke(fuc, 0x10fe20); + ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000002); + ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000000); + } + +// 0x00020034 // 0x0000000a + ram_wr32(fuc, 0x132100, 0x00000001); + + if (mode == 1 && from == 0) { + /* calculate refpll */ + ret = gt215_pll_calc(subdev, &ram->refpll, ram->mempll.refclk, + &N1, NULL, &M1, &P); + if (ret <= 0) { + nvkm_error(subdev, "unable to calc refpll\n"); + return ret ? ret : -ERANGE; + } + + ram_wr32(fuc, 0x10fe20, 0x20010000); + ram_wr32(fuc, 0x137320, 0x00000003); + ram_wr32(fuc, 0x137330, 0x81200006); + ram_wr32(fuc, 0x10fe24, (P << 16) | (N1 << 8) | M1); + ram_wr32(fuc, 0x10fe20, 0x20010001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + + /* calculate mempll */ + ret = gt215_pll_calc(subdev, &ram->mempll, freq, + &N1, NULL, &M1, &P); + if (ret <= 0) { + nvkm_error(subdev, "unable to calc refpll\n"); + return ret ? ret : -ERANGE; + } + + ram_wr32(fuc, 0x10fe20, 0x20010005); + ram_wr32(fuc, 0x132004, (P << 16) | (N1 << 8) | M1); + ram_wr32(fuc, 0x132000, 0x18010101); + ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000); + } else + if (mode == 0) { + ram_wr32(fuc, 0x137300, 0x00000003); + } + + if (from == 0) { + ram_nuke(fuc, 0x10fb04); + ram_mask(fuc, 0x10fb04, 0x0000ffff, 0x00000000); + ram_nuke(fuc, 0x10fb08); + ram_mask(fuc, 0x10fb08, 0x0000ffff, 0x00000000); + ram_wr32(fuc, 0x10f988, 0x2004ff00); + ram_wr32(fuc, 0x10f98c, 0x003fc040); + ram_wr32(fuc, 0x10f990, 0x20012001); + ram_wr32(fuc, 0x10f998, 0x00011a00); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + } else { + ram_wr32(fuc, 0x10f988, 0x20010000); + ram_wr32(fuc, 0x10f98c, 0x00000000); + ram_wr32(fuc, 0x10f990, 0x20012001); + ram_wr32(fuc, 0x10f998, 0x00010a00); + } + + if (from == 0) { +// 0x00020039 // 0x000000ba + } + +// 0x0002003a // 0x00000002 + ram_wr32(fuc, 0x100b0c, 0x00080012); +// 0x00030014 // 0x00000000 // 0x02b5f070 +// 0x00030014 // 0x00010000 // 0x02b5f070 + ram_wr32(fuc, 0x611200, 0x00003300); +// 0x00020034 // 0x0000000a +// 0x00030020 // 0x00000001 // 0x00000000 + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x10f210, 0x00000000); + ram_nsec(fuc, 1000); + if (mode == 0) + gf100_ram_train(fuc, 0x000c1001); + ram_wr32(fuc, 0x10f310, 0x00000001); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f090, 0x00000061); + ram_wr32(fuc, 0x10f090, 0xc000007f); + ram_nsec(fuc, 1000); + + if (from == 0) { + ram_wr32(fuc, 0x10f824, 0x00007fd4); + } else { + ram_wr32(fuc, 0x1373ec, 0x00020404); + } + + if (mode == 0) { + ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000); + ram_mask(fuc, 0x10f200, 0x00008000, 0x00008000); + ram_wr32(fuc, 0x10f830, 0x41500010); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + ram_mask(fuc, 0x132100, 0x00000100, 0x00000100); + ram_wr32(fuc, 0x10f050, 0xff000090); + ram_wr32(fuc, 0x1373ec, 0x00020f0f); + ram_wr32(fuc, 0x1373f0, 0x00000003); + ram_wr32(fuc, 0x137310, 0x81201616); + ram_wr32(fuc, 0x132100, 0x00000001); +// 0x00020039 // 0x000000ba + ram_wr32(fuc, 0x10f830, 0x00300017); + ram_wr32(fuc, 0x1373f0, 0x00000001); + ram_wr32(fuc, 0x10f824, 0x00007e77); + ram_wr32(fuc, 0x132000, 0x18030001); + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); + ram_wr32(fuc, 0x10f210, 0x80000000); + ram_wr32(fuc, 0x10f338, 0x00300220); + ram_wr32(fuc, 0x10f300, 0x0000011d); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f290, 0x02060505); + ram_wr32(fuc, 0x10f294, 0x34208288); + ram_wr32(fuc, 0x10f298, 0x44050411); + ram_wr32(fuc, 0x10f29c, 0x0000114c); + ram_wr32(fuc, 0x10f2a0, 0x42e10069); + ram_wr32(fuc, 0x10f614, 0x40044f77); + ram_wr32(fuc, 0x10f610, 0x40044f77); + ram_wr32(fuc, 0x10f344, 0x00600009); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f348, 0x00700008); + ram_wr32(fuc, 0x61c140, 0x19240000); + ram_wr32(fuc, 0x10f830, 0x00300017); + gf100_ram_train(fuc, 0x80021001); + gf100_ram_train(fuc, 0x80081001); + ram_wr32(fuc, 0x10f340, 0x00500004); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f830, 0x01300017); + ram_wr32(fuc, 0x10f830, 0x00300017); +// 0x00030020 // 0x00000000 // 0x00000000 +// 0x00020034 // 0x0000000b + ram_wr32(fuc, 0x100b0c, 0x00080028); + ram_wr32(fuc, 0x611200, 0x00003330); + } else { + ram_wr32(fuc, 0x10f800, 0x00001800); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x1373ec, 0x00020404); + ram_wr32(fuc, 0x1373f0, 0x00000003); + ram_wr32(fuc, 0x10f830, 0x40700010); + ram_wr32(fuc, 0x10f830, 0x40500010); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x1373f8, 0x00000000); + ram_wr32(fuc, 0x132100, 0x00000101); + ram_wr32(fuc, 0x137310, 0x89201616); + ram_wr32(fuc, 0x10f050, 0xff000090); + ram_wr32(fuc, 0x1373ec, 0x00030404); + ram_wr32(fuc, 0x1373f0, 0x00000002); + // 0x00020039 // 0x00000011 + ram_wr32(fuc, 0x132100, 0x00000001); + ram_wr32(fuc, 0x1373f8, 0x00002000); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f808, 0x7aaa0050); + ram_wr32(fuc, 0x10f830, 0x00500010); + ram_wr32(fuc, 0x10f200, 0x00ce1000); + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); + ram_wr32(fuc, 0x10f210, 0x80000000); + ram_wr32(fuc, 0x10f338, 0x00300200); + ram_wr32(fuc, 0x10f300, 0x0000084d); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f290, 0x0b343825); + ram_wr32(fuc, 0x10f294, 0x3483028e); + ram_wr32(fuc, 0x10f298, 0x440c0600); + ram_wr32(fuc, 0x10f29c, 0x0000214c); + ram_wr32(fuc, 0x10f2a0, 0x42e20069); + ram_wr32(fuc, 0x10f200, 0x00ce0000); + ram_wr32(fuc, 0x10f614, 0x60044e77); + ram_wr32(fuc, 0x10f610, 0x60044e77); + ram_wr32(fuc, 0x10f340, 0x00500000); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f344, 0x00600228); + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f348, 0x00700000); + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x61c140, 0x09a40000); + + gf100_ram_train(fuc, 0x800e1008); + + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f800, 0x00001804); + // 0x00030020 // 0x00000000 // 0x00000000 + // 0x00020034 // 0x0000000b + ram_wr32(fuc, 0x13d8f4, 0x00000000); + ram_wr32(fuc, 0x100b0c, 0x00080028); + ram_wr32(fuc, 0x611200, 0x00003330); + ram_nsec(fuc, 100000); + ram_wr32(fuc, 0x10f9b0, 0x05313f41); + ram_wr32(fuc, 0x10f9b4, 0x00002f50); + + gf100_ram_train(fuc, 0x010c1001); + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000800); +// 0x00020016 // 0x00000000 + + if (mode == 0) + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + + return 0; +} + +int +gf100_ram_prog(struct nvkm_ram *base) +{ + struct gf100_ram *ram = gf100_ram(base); + struct nvkm_device *device = ram->base.fb->subdev.device; + ram_exec(&ram->fuc, nvkm_boolopt(device->cfgopt, "NvMemExec", true)); + return 0; +} + +void +gf100_ram_tidy(struct nvkm_ram *base) +{ + struct gf100_ram *ram = gf100_ram(base); + ram_exec(&ram->fuc, false); +} + +int +gf100_ram_init(struct nvkm_ram *base) +{ + 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, + }; + struct gf100_ram *ram = gf100_ram(base); + struct nvkm_device *device = ram->base.fb->subdev.device; + int i; + + switch (ram->base.type) { + case NVKM_RAM_TYPE_GDDR5: + break; + default: + return 0; + } + + /* prepare for ddr link training, and load training patterns */ + for (i = 0; i < 0x30; i++) { + nvkm_wr32(device, 0x10f968, 0x00000000 | (i << 8)); + nvkm_wr32(device, 0x10f96c, 0x00000000 | (i << 8)); + nvkm_wr32(device, 0x10f920, 0x00000100 | train0[i % 12]); + nvkm_wr32(device, 0x10f924, 0x00000100 | train0[i % 12]); + nvkm_wr32(device, 0x10f918, train1[i % 12]); + nvkm_wr32(device, 0x10f91c, train1[i % 12]); + nvkm_wr32(device, 0x10f920, 0x00000000 | train0[i % 12]); + nvkm_wr32(device, 0x10f924, 0x00000000 | train0[i % 12]); + nvkm_wr32(device, 0x10f918, train1[i % 12]); + nvkm_wr32(device, 0x10f91c, train1[i % 12]); + } + + return 0; +} + +u32 +gf100_ram_probe_fbpa_amount(struct nvkm_device *device, int fbpa) +{ + return nvkm_rd32(device, 0x11020c + (fbpa * 0x1000)); +} + +u32 +gf100_ram_probe_fbp_amount(const struct nvkm_ram_func *func, u32 fbpao, + struct nvkm_device *device, int fbp, int *pltcs) +{ + if (!(fbpao & BIT(fbp))) { + *pltcs = 1; + return func->probe_fbpa_amount(device, fbp); + } + return 0; +} + +u32 +gf100_ram_probe_fbp(const struct nvkm_ram_func *func, + struct nvkm_device *device, int fbp, int *pltcs) +{ + u32 fbpao = nvkm_rd32(device, 0x022554); + return func->probe_fbp_amount(func, fbpao, device, fbp, pltcs); +} + +int +gf100_ram_ctor(const struct nvkm_ram_func *func, struct nvkm_fb *fb, + struct nvkm_ram *ram) +{ + struct nvkm_subdev *subdev = &fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + const u32 rsvd_head = ( 256 * 1024); /* vga memory */ + const u32 rsvd_tail = (1024 * 1024); /* vbios etc */ + enum nvkm_ram_type type = nvkm_fb_bios_memtype(bios); + u32 fbps = nvkm_rd32(device, 0x022438); + u64 total = 0, lcomm = ~0, lower, ubase, usize; + int ret, fbp, ltcs, ltcn = 0; + + nvkm_debug(subdev, "%d FBP(s)\n", fbps); + for (fbp = 0; fbp < fbps; fbp++) { + u32 size = func->probe_fbp(func, device, fbp, <cs); + if (size) { + nvkm_debug(subdev, "FBP %d: %4d MiB, %d LTC(s)\n", + fbp, size, ltcs); + lcomm = min(lcomm, (u64)(size / ltcs) << 20); + total += (u64) size << 20; + ltcn += ltcs; + } else { + nvkm_debug(subdev, "FBP %d: disabled\n", fbp); + } + } + + lower = lcomm * ltcn; + ubase = lcomm + func->upper; + usize = total - lower; + + nvkm_debug(subdev, "Lower: %4lld MiB @ %010llx\n", lower >> 20, 0ULL); + nvkm_debug(subdev, "Upper: %4lld MiB @ %010llx\n", usize >> 20, ubase); + nvkm_debug(subdev, "Total: %4lld MiB\n", total >> 20); + + ret = nvkm_ram_ctor(func, fb, type, total, ram); + if (ret) + return ret; + + nvkm_mm_fini(&ram->vram); + + /* Some GPUs are in what's known as a "mixed memory" configuration. + * + * This is either where some FBPs have more memory than the others, + * or where LTCs have been disabled on a FBP. + */ + if (lower != total) { + /* The common memory amount is addressed normally. */ + ret = nvkm_mm_init(&ram->vram, NVKM_RAM_MM_NORMAL, + rsvd_head >> NVKM_RAM_MM_SHIFT, + (lower - rsvd_head) >> NVKM_RAM_MM_SHIFT, 1); + if (ret) + return ret; + + /* And the rest is much higher in the physical address + * space, and may not be usable for certain operations. + */ + ret = nvkm_mm_init(&ram->vram, NVKM_RAM_MM_MIXED, + ubase >> NVKM_RAM_MM_SHIFT, + (usize - rsvd_tail) >> NVKM_RAM_MM_SHIFT, 1); + if (ret) + return ret; + } else { + /* GPUs without mixed-memory are a lot nicer... */ + ret = nvkm_mm_init(&ram->vram, NVKM_RAM_MM_NORMAL, + rsvd_head >> NVKM_RAM_MM_SHIFT, + (total - rsvd_head - rsvd_tail) >> + NVKM_RAM_MM_SHIFT, 1); + if (ret) + return ret; + } + + return 0; +} + +int +gf100_ram_new_(const struct nvkm_ram_func *func, + struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_subdev *subdev = &fb->subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct gf100_ram *ram; + int ret; + + if (!(ram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + *pram = &ram->base; + + ret = gf100_ram_ctor(func, fb, &ram->base); + if (ret) + return ret; + + ret = nvbios_pll_parse(bios, 0x0c, &ram->refpll); + if (ret) { + nvkm_error(subdev, "mclk refpll data not found\n"); + return ret; + } + + ret = nvbios_pll_parse(bios, 0x04, &ram->mempll); + if (ret) { + nvkm_error(subdev, "mclk pll data not found\n"); + return ret; + } + + 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; +} + +static const struct nvkm_ram_func +gf100_ram = { + .upper = 0x0200000000ULL, + .probe_fbp = gf100_ram_probe_fbp, + .probe_fbp_amount = gf100_ram_probe_fbp_amount, + .probe_fbpa_amount = gf100_ram_probe_fbpa_amount, + .init = gf100_ram_init, + .calc = gf100_ram_calc, + .prog = gf100_ram_prog, + .tidy = gf100_ram_tidy, +}; + +int +gf100_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + return gf100_ram_new_(&gf100_ram, fb, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf108.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf108.c new file mode 100644 index 000000000..d97fa43ef --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf108.c @@ -0,0 +1,60 @@ +/* + * Copyright 2017 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 "ram.h" + +u32 +gf108_ram_probe_fbp_amount(const struct nvkm_ram_func *func, u32 fbpao, + struct nvkm_device *device, int fbp, int *pltcs) +{ + u32 fbpt = nvkm_rd32(device, 0x022438); + u32 fbpat = nvkm_rd32(device, 0x02243c); + u32 fbpas = fbpat / fbpt; + u32 fbpa = fbp * fbpas; + u32 size = 0; + while (fbpas--) { + if (!(fbpao & BIT(fbpa))) + size += func->probe_fbpa_amount(device, fbpa); + fbpa++; + } + *pltcs = 1; + return size; +} + +static const struct nvkm_ram_func +gf108_ram = { + .upper = 0x0200000000ULL, + .probe_fbp = gf100_ram_probe_fbp, + .probe_fbp_amount = gf108_ram_probe_fbp_amount, + .probe_fbpa_amount = gf100_ram_probe_fbpa_amount, + .init = gf100_ram_init, + .calc = gf100_ram_calc, + .prog = gf100_ram_prog, + .tidy = gf100_ram_tidy, +}; + +int +gf108_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + return gf100_ram_new_(&gf108_ram, fb, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c new file mode 100644 index 000000000..2b678b60b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c @@ -0,0 +1,1716 @@ +/* + * 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 + */ +#define gk104_ram(p) container_of((p), struct gk104_ram, base) +#include "ram.h" +#include "ramfuc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gk104_ramfuc { + struct ramfuc base; + + struct nvbios_pll refpll; + struct nvbios_pll mempll; + + struct ramfuc_reg r_gpioMV; + u32 r_funcMV[2]; + struct ramfuc_reg r_gpio2E; + u32 r_func2E[2]; + struct ramfuc_reg r_gpiotrig; + + struct ramfuc_reg r_0x132020; + struct ramfuc_reg r_0x132028; + struct ramfuc_reg r_0x132024; + struct ramfuc_reg r_0x132030; + struct ramfuc_reg r_0x132034; + struct ramfuc_reg r_0x132000; + struct ramfuc_reg r_0x132004; + struct ramfuc_reg r_0x132040; + + struct ramfuc_reg r_0x10f248; + struct ramfuc_reg r_0x10f290; + struct ramfuc_reg r_0x10f294; + struct ramfuc_reg r_0x10f298; + struct ramfuc_reg r_0x10f29c; + struct ramfuc_reg r_0x10f2a0; + struct ramfuc_reg r_0x10f2a4; + struct ramfuc_reg r_0x10f2a8; + struct ramfuc_reg r_0x10f2ac; + struct ramfuc_reg r_0x10f2cc; + struct ramfuc_reg r_0x10f2e8; + struct ramfuc_reg r_0x10f250; + struct ramfuc_reg r_0x10f24c; + struct ramfuc_reg r_0x10fec4; + struct ramfuc_reg r_0x10fec8; + struct ramfuc_reg r_0x10f604; + struct ramfuc_reg r_0x10f614; + struct ramfuc_reg r_0x10f610; + struct ramfuc_reg r_0x100770; + struct ramfuc_reg r_0x100778; + struct ramfuc_reg r_0x10f224; + + struct ramfuc_reg r_0x10f870; + struct ramfuc_reg r_0x10f698; + struct ramfuc_reg r_0x10f694; + struct ramfuc_reg r_0x10f6b8; + struct ramfuc_reg r_0x10f808; + struct ramfuc_reg r_0x10f670; + struct ramfuc_reg r_0x10f60c; + struct ramfuc_reg r_0x10f830; + struct ramfuc_reg r_0x1373ec; + struct ramfuc_reg r_0x10f800; + struct ramfuc_reg r_0x10f82c; + + struct ramfuc_reg r_0x10f978; + struct ramfuc_reg r_0x10f910; + struct ramfuc_reg r_0x10f914; + + struct ramfuc_reg r_mr[16]; /* MR0 - MR8, MR15 */ + + struct ramfuc_reg r_0x62c000; + + struct ramfuc_reg r_0x10f200; + + struct ramfuc_reg r_0x10f210; + struct ramfuc_reg r_0x10f310; + struct ramfuc_reg r_0x10f314; + struct ramfuc_reg r_0x10f318; + struct ramfuc_reg r_0x10f090; + struct ramfuc_reg r_0x10f69c; + struct ramfuc_reg r_0x10f824; + struct ramfuc_reg r_0x1373f0; + struct ramfuc_reg r_0x1373f4; + struct ramfuc_reg r_0x137320; + struct ramfuc_reg r_0x10f65c; + struct ramfuc_reg r_0x10f6bc; + struct ramfuc_reg r_0x100710; + struct ramfuc_reg r_0x100750; +}; + +struct gk104_ram { + struct nvkm_ram base; + struct gk104_ramfuc fuc; + + struct list_head cfg; + u32 parts; + u32 pmask; + u32 pnuts; + + struct nvbios_ramcfg diff; + int from; + int mode; + int N1, fN1, M1, P1; + int N2, M2, P2; +}; + +/******************************************************************************* + * GDDR5 + ******************************************************************************/ +static void +gk104_ram_train(struct gk104_ramfuc *fuc, u32 mask, u32 data) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + u32 addr = 0x110974, i; + + ram_mask(fuc, 0x10f910, mask, data); + ram_mask(fuc, 0x10f914, mask, data); + + for (i = 0; (data & 0x80000000) && i < ram->parts; addr += 0x1000, i++) { + if (ram->pmask & (1 << i)) + continue; + ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000); + } +} + +static void +r1373f4_init(struct gk104_ramfuc *fuc) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + const u32 mcoef = ((--ram->P2 << 28) | (ram->N2 << 8) | ram->M2); + const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1); + const u32 runk0 = ram->fN1 << 16; + const u32 runk1 = ram->fN1; + + if (ram->from == 2) { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100); + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010); + } else { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010); + } + + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000); + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000); + + /* (re)program refpll, if required */ + if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef || + (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) { + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000000); + ram_wr32(fuc, 0x137320, 0x00000000); + ram_mask(fuc, 0x132030, 0xffff0000, runk0); + ram_mask(fuc, 0x132034, 0x0000ffff, runk1); + ram_wr32(fuc, 0x132024, rcoef); + ram_mask(fuc, 0x132028, 0x00080000, 0x00080000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x132028, 0x00080000, 0x00000000); + } + + /* (re)program mempll, if required */ + if (ram->mode == 2) { + ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + ram_mask(fuc, 0x132000, 0x80000000, 0x80000000); + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132004, 0x103fffff, mcoef); + ram_mask(fuc, 0x132000, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000); + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100); + } else { + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010100); + } + + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010); +} + +static void +r1373f4_fini(struct gk104_ramfuc *fuc) +{ + struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc); + struct nvkm_ram_data *next = ram->base.next; + u8 v0 = next->bios.ramcfg_11_03_c0; + u8 v1 = next->bios.ramcfg_11_03_30; + u32 tmp; + + tmp = ram_rd32(fuc, 0x1373ec) & ~0x00030000; + ram_wr32(fuc, 0x1373ec, tmp | (v1 << 16)); + ram_mask(fuc, 0x1373f0, (~ram->mode & 3), 0x00000000); + if (ram->mode == 2) { + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000002); + ram_mask(fuc, 0x1373f4, 0x00001100, 0x00000000); + } else { + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000001); + ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + } + ram_mask(fuc, 0x10f800, 0x00000030, (v0 ^ v1) << 4); +} + +static void +gk104_ram_nuts(struct gk104_ram *ram, struct ramfuc_reg *reg, + u32 _mask, u32 _data, u32 _copy) +{ + struct nvkm_fb *fb = ram->base.fb; + struct ramfuc *fuc = &ram->fuc.base; + struct nvkm_device *device = fb->subdev.device; + u32 addr = 0x110000 + (reg->addr & 0xfff); + u32 mask = _mask | _copy; + u32 data = (_data & _mask) | (reg->data & _copy); + u32 i; + + for (i = 0; i < 16; i++, addr += 0x1000) { + if (ram->pnuts & (1 << i)) { + u32 prev = nvkm_rd32(device, addr); + u32 next = (prev & ~mask) | data; + nvkm_memx_wr32(fuc->memx, addr, next); + } + } +} +#define ram_nuts(s,r,m,d,c) \ + gk104_ram_nuts((s), &(s)->fuc.r_##r, (m), (d), (c)) + +static int +gk104_ram_calc_gddr5(struct gk104_ram *ram, u32 freq) +{ + struct gk104_ramfuc *fuc = &ram->fuc; + struct nvkm_ram_data *next = ram->base.next; + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); + + if (ram->base.fb->subdev.device->disp) + ram_wr32(fuc, 0x62c000, 0x0f0f0000); + + /* MR1: turn termination on early, for some reason.. */ + if ((ram->base.mr[1] & 0x03c) != 0x030) { + ram_mask(fuc, mr[1], 0x03c, ram->base.mr[1] & 0x03c); + ram_nuts(ram, mr[1], 0x03c, ram->base.mr1_nuts & 0x03c, 0x000); + } + + if (vc == 1 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + + gk104_ram_train(fuc, 0x01020000, 0x000c0000); + + ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */ + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_nsec(fuc, 1000); + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_wr32(fuc, 0x10f090, 0x00000061); + ram_wr32(fuc, 0x10f090, 0xc000007f); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f698, 0x00000000); + ram_wr32(fuc, 0x10f69c, 0x00000000); + + /*XXX: there does appear to be some kind of condition here, simply + * modifying these bits in the vbios from the default pl0 + * entries shows no change. however, the data does appear to + * be correct and may be required for the transition back + */ + mask = 0x800f07e0; + data = 0x00030000; + if (ram_rd32(fuc, 0x10f978) & 0x00800000) + data |= 0x00040000; + + if (1) { + data |= 0x800807e0; + switch (next->bios.ramcfg_11_03_c0) { + case 3: data &= ~0x00000040; break; + case 2: data &= ~0x00000100; break; + case 1: data &= ~0x80000000; break; + case 0: data &= ~0x00000400; break; + } + + switch (next->bios.ramcfg_11_03_30) { + case 3: data &= ~0x00000020; break; + case 2: data &= ~0x00000080; break; + case 1: data &= ~0x00080000; break; + case 0: data &= ~0x00000200; break; + } + } + + if (next->bios.ramcfg_11_02_80) + mask |= 0x03000000; + if (next->bios.ramcfg_11_02_40) + mask |= 0x00002000; + if (next->bios.ramcfg_11_07_10) + mask |= 0x00004000; + if (next->bios.ramcfg_11_07_08) + mask |= 0x00000003; + else { + mask |= 0x34000000; + if (ram_rd32(fuc, 0x10f978) & 0x00800000) + mask |= 0x40000000; + } + ram_mask(fuc, 0x10f824, mask, data); + + ram_mask(fuc, 0x132040, 0x00010000, 0x00000000); + + if (ram->from == 2 && ram->mode != 2) { + ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000); + ram_mask(fuc, 0x10f200, 0x18008000, 0x00008000); + ram_mask(fuc, 0x10f800, 0x00000000, 0x00000004); + ram_mask(fuc, 0x10f830, 0x00008000, 0x01040010); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + r1373f4_init(fuc); + ram_mask(fuc, 0x1373f0, 0x00000002, 0x00000001); + r1373f4_fini(fuc); + ram_mask(fuc, 0x10f830, 0x00c00000, 0x00240001); + } else + if (ram->from != 2 && ram->mode != 2) { + r1373f4_init(fuc); + r1373f4_fini(fuc); + } + + if (ram_have(fuc, gpioMV)) { + u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]); + if (temp != ram_rd32(fuc, gpioMV)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 64000); + } + } + + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->from != 2 && ram->mode == 2) { + if (0 /*XXX: Titan */) + ram_mask(fuc, 0x10f200, 0x18000000, 0x18000000); + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000); + ram_mask(fuc, 0x1373f0, 0x00000000, 0x00000002); + ram_mask(fuc, 0x10f830, 0x00800001, 0x00408010); + r1373f4_init(fuc); + r1373f4_fini(fuc); + ram_mask(fuc, 0x10f808, 0x00000000, 0x00080000); + ram_mask(fuc, 0x10f200, 0x00808000, 0x00800000); + } else + if (ram->from == 2 && ram->mode == 2) { + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000); + r1373f4_init(fuc); + r1373f4_fini(fuc); + } + + if (ram->mode != 2) /*XXX*/ { + if (next->bios.ramcfg_11_07_40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09); + + if (!next->bios.ramcfg_11_07_08 && !next->bios.ramcfg_11_07_04) { + ram_wr32(fuc, 0x10f698, 0x01010101 * next->bios.ramcfg_11_04); + ram_wr32(fuc, 0x10f69c, 0x01010101 * next->bios.ramcfg_11_04); + } else + if (!next->bios.ramcfg_11_07_08) { + ram_wr32(fuc, 0x10f698, 0x00000000); + ram_wr32(fuc, 0x10f69c, 0x00000000); + } + + if (ram->mode != 2) { + u32 data = 0x01000100 * next->bios.ramcfg_11_04; + ram_nuke(fuc, 0x10f694); + ram_mask(fuc, 0x10f694, 0xff00ff00, data); + } + + if (ram->mode == 2 && next->bios.ramcfg_11_08_10) + data = 0x00000080; + else + data = 0x00000000; + ram_mask(fuc, 0x10f60c, 0x00000080, data); + + mask = 0x00070000; + data = 0x00000000; + if (!next->bios.ramcfg_11_02_80) + data |= 0x03000000; + if (!next->bios.ramcfg_11_02_40) + data |= 0x00002000; + if (!next->bios.ramcfg_11_07_10) + data |= 0x00004000; + if (!next->bios.ramcfg_11_07_08) + data |= 0x00000003; + else + data |= 0x74000000; + ram_mask(fuc, 0x10f824, mask, data); + + if (next->bios.ramcfg_11_01_08) + data = 0x00000000; + else + data = 0x00001000; + ram_mask(fuc, 0x10f200, 0x00001000, data); + + if (ram_rd32(fuc, 0x10f670) & 0x80000000) { + ram_nsec(fuc, 10000); + ram_mask(fuc, 0x10f670, 0x80000000, 0x00000000); + } + + if (next->bios.ramcfg_11_08_01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + data = 0x00000000; + if (next->bios.ramcfg_11_08_08) + data |= 0x00002000; + if (next->bios.ramcfg_11_08_04) + data |= 0x00001000; + if (next->bios.ramcfg_11_08_02) + data |= 0x00004000; + ram_mask(fuc, 0x10f830, 0x00007000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]); + ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]); + ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]); + ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]); + ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]); + ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]); + ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]); + ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]); + ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]); + ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]); + ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_08_20) { + if (next->bios.ramcfg_11_08_20) + data |= 0x01000000; + mask |= 0x01000000; + } + ram_mask(fuc, 0x10f200, mask, data); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_02_03) { + data |= next->bios.ramcfg_11_02_03 << 8; + mask |= 0x00000300; + } + if (ram->diff.ramcfg_11_01_10) { + if (next->bios.ramcfg_11_01_10) + data |= 0x70000000; + mask |= 0x70000000; + } + ram_mask(fuc, 0x10f604, mask, data); + + data = mask = 0x00000000; + if (ram->diff.timing_20_30_07) { + data |= next->bios.timing_20_30_07 << 28; + mask |= 0x70000000; + } + if (ram->diff.ramcfg_11_01_01) { + if (next->bios.ramcfg_11_01_01) + data |= 0x00000100; + mask |= 0x00000100; + } + ram_mask(fuc, 0x10f614, mask, data); + + data = mask = 0x00000000; + if (ram->diff.timing_20_30_07) { + data |= next->bios.timing_20_30_07 << 28; + mask |= 0x70000000; + } + if (ram->diff.ramcfg_11_01_02) { + if (next->bios.ramcfg_11_01_02) + data |= 0x00000100; + mask |= 0x00000100; + } + ram_mask(fuc, 0x10f610, mask, data); + + mask = 0x33f00000; + data = 0x00000000; + if (!next->bios.ramcfg_11_01_04) + data |= 0x20200000; + if (!next->bios.ramcfg_11_07_80) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if (next->bios.ramcfg_11_03_f0) { + if (next->bios.rammap_11_08_0c) { + if (!next->bios.ramcfg_11_07_80) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x00000004; + } + } else { + mask |= 0x40000020; + data |= 0x00000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); + + data = mask = 0x00000000; + if (ram->diff.ramcfg_11_02_03) { + data |= next->bios.ramcfg_11_02_03; + mask |= 0x00000003; + } + if (ram->diff.ramcfg_11_01_10) { + if (next->bios.ramcfg_11_01_10) + data |= 0x00000004; + mask |= 0x00000004; + } + + if ((ram_mask(fuc, 0x100770, mask, data) & mask & 4) != (data & 4)) { + ram_mask(fuc, 0x100750, 0x00000008, 0x00000008); + ram_wr32(fuc, 0x100710, 0x00000000); + ram_wait(fuc, 0x100710, 0x80000000, 0x80000000, 200000); + } + + data = next->bios.timing_20_30_07 << 8; + if (next->bios.ramcfg_11_01_01) + data |= 0x80000000; + ram_mask(fuc, 0x100778, 0x00000700, data); + + ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4); + data = (next->bios.timing[10] & 0x7f000000) >> 24; + if (data < next->bios.timing_20_2c_1fc0) + data = next->bios.timing_20_2c_1fc0; + ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); + ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16); + + ram_mask(fuc, 0x10fec4, 0x041e0f07, next->bios.timing_20_31_0800 << 26 | + next->bios.timing_20_31_0780 << 17 | + next->bios.timing_20_31_0078 << 8 | + next->bios.timing_20_31_0007); + ram_mask(fuc, 0x10fec8, 0x00000027, next->bios.timing_20_31_8000 << 5 | + next->bios.timing_20_31_7000); + + ram_wr32(fuc, 0x10f090, 0x4000007e); + ram_nsec(fuc, 2000); + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ + + if (next->bios.ramcfg_11_08_10 && (ram->mode == 2) /*XXX*/) { + u32 temp = ram_mask(fuc, 0x10f294, 0xff000000, 0x24000000); + gk104_ram_train(fuc, 0xbc0e0000, 0xa4010000); /*XXX*/ + ram_nsec(fuc, 1000); + ram_wr32(fuc, 0x10f294, temp); + } + + ram_mask(fuc, mr[3], 0xfff, ram->base.mr[3]); + ram_wr32(fuc, mr[0], ram->base.mr[0]); + ram_mask(fuc, mr[8], 0xfff, ram->base.mr[8]); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[1], 0xfff, ram->base.mr[1]); + ram_mask(fuc, mr[5], 0xfff, ram->base.mr[5] & ~0x004); /* LP3 later */ + ram_mask(fuc, mr[6], 0xfff, ram->base.mr[6]); + ram_mask(fuc, mr[7], 0xfff, ram->base.mr[7]); + + if (vc == 0 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + ram_nuts(ram, 0x10f200, 0x18808800, 0x00000000, 0x18808800); + + data = ram_rd32(fuc, 0x10f978); + data &= ~0x00046144; + data |= 0x0000000b; + if (!next->bios.ramcfg_11_07_08) { + if (!next->bios.ramcfg_11_07_04) + data |= 0x0000200c; + else + data |= 0x00000000; + } else { + data |= 0x00040044; + } + ram_wr32(fuc, 0x10f978, data); + + if (ram->mode == 1) { + data = ram_rd32(fuc, 0x10f830) | 0x00000001; + ram_wr32(fuc, 0x10f830, data); + } + + if (!next->bios.ramcfg_11_07_08) { + data = 0x88020000; + if ( next->bios.ramcfg_11_07_04) + data |= 0x10000000; + if (!next->bios.rammap_11_08_10) + data |= 0x00080000; + } else { + data = 0xa40e0000; + } + gk104_ram_train(fuc, 0xbc0f0000, data); + if (1) /* XXX: not always? */ + ram_nsec(fuc, 1000); + + if (ram->mode == 2) { /*XXX*/ + ram_mask(fuc, 0x10f800, 0x00000004, 0x00000004); + } + + /* LP3 */ + if (ram_mask(fuc, mr[5], 0x004, ram->base.mr[5]) != ram->base.mr[5]) + ram_nsec(fuc, 1000); + + if (ram->mode != 2) { + ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + } + + if (next->bios.ramcfg_11_07_02) + gk104_ram_train(fuc, 0x80020000, 0x01000000); + + ram_unblock(fuc); + + if (ram->base.fb->subdev.device->disp) + ram_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (next->bios.rammap_11_08_01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + ram_nuts(ram, 0x10f200, 0x18808800, data, 0x18808800); + return 0; +} + +/******************************************************************************* + * DDR3 + ******************************************************************************/ + +static void +nvkm_sddr3_dll_reset(struct gk104_ramfuc *fuc) +{ + ram_nuke(fuc, mr[0]); + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_mask(fuc, mr[0], 0x100, 0x000); +} + +static void +nvkm_sddr3_dll_disable(struct gk104_ramfuc *fuc) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x1)) { + ram_mask(fuc, mr[1], 0x1, 0x1); + ram_nsec(fuc, 1000); + } +} + +static int +gk104_ram_calc_sddr3(struct gk104_ram *ram, u32 freq) +{ + struct gk104_ramfuc *fuc = &ram->fuc; + const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1); + const u32 runk0 = ram->fN1 << 16; + const u32 runk1 = ram->fN1; + struct nvkm_ram_data *next = ram->base.next; + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; + u32 mask, data; + + ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); + + if (ram->base.fb->subdev.device->disp) + ram_wr32(fuc, 0x62c000, 0x0f0f0000); + + if (vc == 1 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); + if (next->bios.ramcfg_11_03_f0) + ram_mask(fuc, 0x10f808, 0x04000000, 0x04000000); + + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + + if (next->bios.ramcfg_DLLoff) + nvkm_sddr3_dll_disable(fuc); + + ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f090, 0x00000060); + ram_wr32(fuc, 0x10f090, 0xc000007e); + + /*XXX: there does appear to be some kind of condition here, simply + * modifying these bits in the vbios from the default pl0 + * entries shows no change. however, the data does appear to + * be correct and may be required for the transition back + */ + mask = 0x00010000; + data = 0x00010000; + + if (1) { + mask |= 0x800807e0; + data |= 0x800807e0; + switch (next->bios.ramcfg_11_03_c0) { + case 3: data &= ~0x00000040; break; + case 2: data &= ~0x00000100; break; + case 1: data &= ~0x80000000; break; + case 0: data &= ~0x00000400; break; + } + + switch (next->bios.ramcfg_11_03_30) { + case 3: data &= ~0x00000020; break; + case 2: data &= ~0x00000080; break; + case 1: data &= ~0x00080000; break; + case 0: data &= ~0x00000200; break; + } + } + + if (next->bios.ramcfg_11_02_80) + mask |= 0x03000000; + if (next->bios.ramcfg_11_02_40) + mask |= 0x00002000; + if (next->bios.ramcfg_11_07_10) + mask |= 0x00004000; + if (next->bios.ramcfg_11_07_08) + mask |= 0x00000003; + else + mask |= 0x14000000; + ram_mask(fuc, 0x10f824, mask, data); + + ram_mask(fuc, 0x132040, 0x00010000, 0x00000000); + + ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010); + data = ram_rd32(fuc, 0x1373ec) & ~0x00030000; + data |= next->bios.ramcfg_11_03_30 << 16; + ram_wr32(fuc, 0x1373ec, data); + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000); + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000); + + /* (re)program refpll, if required */ + if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef || + (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) { + ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000000); + ram_wr32(fuc, 0x137320, 0x00000000); + ram_mask(fuc, 0x132030, 0xffff0000, runk0); + ram_mask(fuc, 0x132034, 0x0000ffff, runk1); + ram_wr32(fuc, 0x132024, rcoef); + ram_mask(fuc, 0x132028, 0x00080000, 0x00080000); + ram_mask(fuc, 0x132020, 0x00000001, 0x00000001); + ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x132028, 0x00080000, 0x00000000); + } + + ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000010); + ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000001); + ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + + if (ram_have(fuc, gpioMV)) { + u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]); + if (temp != ram_rd32(fuc, gpioMV)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 64000); + } + } + + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { + ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); + ram_nsec(fuc, 20000); + } + + if (ram->mode != 2) /*XXX*/ { + if (next->bios.ramcfg_11_07_40) + ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000); + } + + ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c); + ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09); + ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09); + + mask = 0x00010000; + data = 0x00000000; + if (!next->bios.ramcfg_11_02_80) + data |= 0x03000000; + if (!next->bios.ramcfg_11_02_40) + data |= 0x00002000; + if (!next->bios.ramcfg_11_07_10) + data |= 0x00004000; + if (!next->bios.ramcfg_11_07_08) + data |= 0x00000003; + else + data |= 0x14000000; + ram_mask(fuc, 0x10f824, mask, data); + ram_nsec(fuc, 1000); + + if (next->bios.ramcfg_11_08_01) + data = 0x00100000; + else + data = 0x00000000; + ram_mask(fuc, 0x10f82c, 0x00100000, data); + + /* PFB timing */ + ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]); + ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]); + ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]); + ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]); + ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]); + ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]); + ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]); + ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]); + ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]); + ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]); + ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); + + mask = 0x33f00000; + data = 0x00000000; + if (!next->bios.ramcfg_11_01_04) + data |= 0x20200000; + if (!next->bios.ramcfg_11_07_80) + data |= 0x12800000; + /*XXX: see note above about there probably being some condition + * for the 10f824 stuff that uses ramcfg 3... + */ + if (next->bios.ramcfg_11_03_f0) { + if (next->bios.rammap_11_08_0c) { + if (!next->bios.ramcfg_11_07_80) + mask |= 0x00000020; + else + data |= 0x00000020; + mask |= 0x08000004; + } + data |= 0x04000000; + } else { + mask |= 0x44000020; + data |= 0x08000004; + } + + ram_mask(fuc, 0x10f808, mask, data); + + ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); + + ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4); + + data = (next->bios.timing[10] & 0x7f000000) >> 24; + if (data < next->bios.timing_20_2c_1fc0) + data = next->bios.timing_20_2c_1fc0; + ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); + + ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16); + + ram_wr32(fuc, 0x10f090, 0x4000007f); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ + ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ + ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ + ram_nsec(fuc, 1000); + + if (!next->bios.ramcfg_DLLoff) { + ram_mask(fuc, mr[1], 0x1, 0x0); + nvkm_sddr3_dll_reset(fuc); + } + + ram_mask(fuc, mr[2], 0x00000fff, ram->base.mr[2]); + ram_mask(fuc, mr[1], 0xffffffff, ram->base.mr[1]); + ram_wr32(fuc, mr[0], ram->base.mr[0]); + ram_nsec(fuc, 1000); + + if (!next->bios.ramcfg_DLLoff) { + nvkm_sddr3_dll_reset(fuc); + ram_nsec(fuc, 1000); + } + + if (vc == 0 && ram_have(fuc, gpio2E)) { + u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]); + if (temp != ram_rd32(fuc, gpio2E)) { + ram_wr32(fuc, gpiotrig, 1); + ram_nsec(fuc, 20000); + } + } + + if (ram->mode != 2) { + ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000); + ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000); + } + + ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */ + ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); + ram_nsec(fuc, 1000); + + ram_unblock(fuc); + + if (ram->base.fb->subdev.device->disp) + ram_wr32(fuc, 0x62c000, 0x0f0f0f00); + + if (next->bios.rammap_11_08_01) + data = 0x00000800; + else + data = 0x00000000; + ram_mask(fuc, 0x10f200, 0x00000800, data); + return 0; +} + +/******************************************************************************* + * main hooks + ******************************************************************************/ + +static int +gk104_ram_calc_data(struct gk104_ram *ram, u32 khz, struct nvkm_ram_data *data) +{ + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_ram_data *cfg; + u32 mhz = khz / 1000; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) { + *data = *cfg; + data->freq = khz; + return 0; + } + } + + nvkm_error(subdev, "ramcfg data for %dMHz not found\n", mhz); + return -EINVAL; +} + +static int +gk104_calc_pll_output(int fN, int M, int N, int P, int clk) +{ + return ((clk * N) + (((u16)(fN + 4096) * clk) >> 13)) / (M * P); +} + +static int +gk104_pll_calc_hiclk(int target_khz, int crystal, + int *N1, int *fN1, int *M1, int *P1, + int *N2, int *M2, int *P2) +{ + int best_err = target_khz, p_ref, n_ref; + bool upper = false; + + *M1 = 1; + /* M has to be 1, otherwise it gets unstable */ + *M2 = 1; + /* can be 1 or 2, sticking with 1 for simplicity */ + *P2 = 1; + + for (p_ref = 0x7; p_ref >= 0x5; --p_ref) { + for (n_ref = 0x25; n_ref <= 0x2b; ++n_ref) { + int cur_N, cur_clk, cur_err; + + cur_clk = gk104_calc_pll_output(0, 1, n_ref, p_ref, crystal); + cur_N = target_khz / cur_clk; + cur_err = target_khz + - gk104_calc_pll_output(0xf000, 1, cur_N, 1, cur_clk); + + /* we found a better combination */ + if (cur_err < best_err) { + best_err = cur_err; + *N2 = cur_N; + *N1 = n_ref; + *P1 = p_ref; + upper = false; + } + + cur_N += 1; + cur_err = gk104_calc_pll_output(0xf000, 1, cur_N, 1, cur_clk) + - target_khz; + if (cur_err < best_err) { + best_err = cur_err; + *N2 = cur_N; + *N1 = n_ref; + *P1 = p_ref; + upper = true; + } + } + } + + /* adjust fN to get closer to the target clock */ + *fN1 = (u16)((((best_err / *N2 * *P2) * (*P1 * *M1)) << 13) / crystal); + if (upper) + *fN1 = (u16)(1 - *fN1); + + return gk104_calc_pll_output(*fN1, 1, *N1, *P1, crystal); +} + +static int +gk104_ram_calc_xits(struct gk104_ram *ram, struct nvkm_ram_data *next) +{ + struct gk104_ramfuc *fuc = &ram->fuc; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + int refclk, i; + int ret; + + ret = ram_init(fuc, ram->base.fb); + if (ret) + return ret; + + ram->mode = (next->freq > fuc->refpll.vco1.max_freq) ? 2 : 1; + ram->from = ram_rd32(fuc, 0x1373f4) & 0x0000000f; + + /* XXX: this is *not* what nvidia do. on fermi nvidia generally + * select, based on some unknown condition, one of the two possible + * reference frequencies listed in the vbios table for mempll and + * program refpll to that frequency. + * + * so far, i've seen very weird values being chosen by nvidia on + * kepler boards, no idea how/why they're chosen. + */ + refclk = next->freq; + if (ram->mode == 2) { + ret = gk104_pll_calc_hiclk(next->freq, subdev->device->crystal, + &ram->N1, &ram->fN1, &ram->M1, &ram->P1, + &ram->N2, &ram->M2, &ram->P2); + fuc->mempll.refclk = ret; + if (ret <= 0) { + nvkm_error(subdev, "unable to calc plls\n"); + return -EINVAL; + } + nvkm_debug(subdev, "successfully calced PLLs for clock %i kHz" + " (refclock: %i kHz)\n", next->freq, ret); + } else { + /* calculate refpll coefficients */ + ret = gt215_pll_calc(subdev, &fuc->refpll, refclk, &ram->N1, + &ram->fN1, &ram->M1, &ram->P1); + fuc->mempll.refclk = ret; + if (ret <= 0) { + nvkm_error(subdev, "unable to calc refpll\n"); + return -EINVAL; + } + } + + for (i = 0; i < ARRAY_SIZE(fuc->r_mr); i++) { + if (ram_have(fuc, mr[i])) + ram->base.mr[i] = ram_rd32(fuc, mr[i]); + } + ram->base.freq = next->freq; + + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR3: + ret = nvkm_sddr3_calc(&ram->base); + if (ret == 0) + ret = gk104_ram_calc_sddr3(ram, next->freq); + break; + case NVKM_RAM_TYPE_GDDR5: + ret = nvkm_gddr5_calc(&ram->base, ram->pnuts != 0); + if (ret == 0) + ret = gk104_ram_calc_gddr5(ram, next->freq); + break; + default: + ret = -ENOSYS; + break; + } + + return ret; +} + +int +gk104_ram_calc(struct nvkm_ram *base, u32 freq) +{ + struct gk104_ram *ram = gk104_ram(base); + struct nvkm_clk *clk = ram->base.fb->subdev.device->clk; + struct nvkm_ram_data *xits = &ram->base.xition; + struct nvkm_ram_data *copy; + int ret; + + if (ram->base.next == NULL) { + ret = gk104_ram_calc_data(ram, + nvkm_clk_read(clk, nv_clk_src_mem), + &ram->base.former); + if (ret) + return ret; + + ret = gk104_ram_calc_data(ram, freq, &ram->base.target); + if (ret) + return ret; + + if (ram->base.target.freq < ram->base.former.freq) { + *xits = ram->base.target; + copy = &ram->base.former; + } else { + *xits = ram->base.former; + copy = &ram->base.target; + } + + xits->bios.ramcfg_11_02_04 = copy->bios.ramcfg_11_02_04; + xits->bios.ramcfg_11_02_03 = copy->bios.ramcfg_11_02_03; + xits->bios.timing_20_30_07 = copy->bios.timing_20_30_07; + + ram->base.next = &ram->base.target; + if (memcmp(xits, &ram->base.former, sizeof(xits->bios))) + ram->base.next = &ram->base.xition; + } else { + BUG_ON(ram->base.next != &ram->base.xition); + ram->base.next = &ram->base.target; + } + + return gk104_ram_calc_xits(ram, ram->base.next); +} + +static void +gk104_ram_prog_0(struct gk104_ram *ram, u32 freq) +{ + struct nvkm_device *device = ram->base.fb->subdev.device; + struct nvkm_ram_data *cfg; + u32 mhz = freq / 1000; + u32 mask, data; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) + break; + } + + if (&cfg->head == &ram->cfg) + return; + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_03fe) { + data |= cfg->bios.rammap_11_0a_03fe << 12; + mask |= 0x001ff000; + } + if (ram->diff.rammap_11_09_01ff) { + data |= cfg->bios.rammap_11_09_01ff; + mask |= 0x000001ff; + } + nvkm_mask(device, 0x10f468, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0400) { + data |= cfg->bios.rammap_11_0a_0400; + mask |= 0x00000001; + } + nvkm_mask(device, 0x10f420, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0800) { + data |= cfg->bios.rammap_11_0a_0800; + mask |= 0x00000001; + } + nvkm_mask(device, 0x10f430, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_01f0) { + data |= cfg->bios.rammap_11_0b_01f0; + mask |= 0x0000001f; + } + nvkm_mask(device, 0x10f400, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_0200) { + data |= cfg->bios.rammap_11_0b_0200 << 9; + mask |= 0x00000200; + } + nvkm_mask(device, 0x10f410, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0d) { + data |= cfg->bios.rammap_11_0d << 16; + mask |= 0x00ff0000; + } + if (ram->diff.rammap_11_0f) { + data |= cfg->bios.rammap_11_0f << 8; + mask |= 0x0000ff00; + } + nvkm_mask(device, 0x10f440, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0e) { + data |= cfg->bios.rammap_11_0e << 8; + mask |= 0x0000ff00; + } + if (ram->diff.rammap_11_0b_0800) { + data |= cfg->bios.rammap_11_0b_0800 << 7; + mask |= 0x00000080; + } + if (ram->diff.rammap_11_0b_0400) { + data |= cfg->bios.rammap_11_0b_0400 << 5; + mask |= 0x00000020; + } + nvkm_mask(device, 0x10f444, mask, data); +} + +int +gk104_ram_prog(struct nvkm_ram *base) +{ + struct gk104_ram *ram = gk104_ram(base); + struct gk104_ramfuc *fuc = &ram->fuc; + struct nvkm_device *device = ram->base.fb->subdev.device; + struct nvkm_ram_data *next = ram->base.next; + + if (!nvkm_boolopt(device->cfgopt, "NvMemExec", true)) { + ram_exec(fuc, false); + return (ram->base.next == &ram->base.xition); + } + + gk104_ram_prog_0(ram, 1000); + ram_exec(fuc, true); + gk104_ram_prog_0(ram, next->freq); + + return (ram->base.next == &ram->base.xition); +} + +void +gk104_ram_tidy(struct nvkm_ram *base) +{ + struct gk104_ram *ram = gk104_ram(base); + ram->base.next = NULL; + ram_exec(&ram->fuc, false); +} + +struct gk104_ram_train { + u16 mask; + struct nvbios_M0209S remap; + struct nvbios_M0209S type00; + struct nvbios_M0209S type01; + struct nvbios_M0209S type04; + struct nvbios_M0209S type06; + struct nvbios_M0209S type07; + struct nvbios_M0209S type08; + struct nvbios_M0209S type09; +}; + +static int +gk104_ram_train_type(struct nvkm_ram *ram, int i, u8 ramcfg, + struct gk104_ram_train *train) +{ + struct nvkm_bios *bios = ram->fb->subdev.device->bios; + struct nvbios_M0205E M0205E; + struct nvbios_M0205S M0205S; + struct nvbios_M0209E M0209E; + struct nvbios_M0209S *remap = &train->remap; + struct nvbios_M0209S *value; + u8 ver, hdr, cnt, len; + u32 data; + + /* determine type of data for this index */ + if (!(data = nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E))) + return -ENOENT; + + switch (M0205E.type) { + case 0x00: value = &train->type00; break; + case 0x01: value = &train->type01; break; + case 0x04: value = &train->type04; break; + case 0x06: value = &train->type06; break; + case 0x07: value = &train->type07; break; + case 0x08: value = &train->type08; break; + case 0x09: value = &train->type09; break; + default: + return 0; + } + + /* training data index determined by ramcfg strap */ + if (!(data = nvbios_M0205Sp(bios, i, ramcfg, &ver, &hdr, &M0205S))) + return -EINVAL; + i = M0205S.data; + + /* training data format information */ + if (!(data = nvbios_M0209Ep(bios, i, &ver, &hdr, &cnt, &len, &M0209E))) + return -EINVAL; + + /* ... and the raw data */ + if (!(data = nvbios_M0209Sp(bios, i, 0, &ver, &hdr, value))) + return -EINVAL; + + if (M0209E.v02_07 == 2) { + /* of course! why wouldn't we have a pointer to another entry + * in the same table, and use the first one as an array of + * remap indices... + */ + if (!(data = nvbios_M0209Sp(bios, M0209E.v03, 0, &ver, &hdr, + remap))) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(value->data); i++) + value->data[i] = remap->data[value->data[i]]; + } else + if (M0209E.v02_07 != 1) + return -EINVAL; + + train->mask |= 1 << M0205E.type; + return 0; +} + +static int +gk104_ram_train_init_0(struct nvkm_ram *ram, struct gk104_ram_train *train) +{ + struct nvkm_subdev *subdev = &ram->fb->subdev; + struct nvkm_device *device = subdev->device; + int i, j; + + if ((train->mask & 0x03d3) != 0x03d3) { + nvkm_warn(subdev, "missing link training data\n"); + return -EINVAL; + } + + for (i = 0; i < 0x30; i++) { + for (j = 0; j < 8; j += 4) { + nvkm_wr32(device, 0x10f968 + j, 0x00000000 | (i << 8)); + nvkm_wr32(device, 0x10f920 + j, 0x00000000 | + train->type08.data[i] << 4 | + train->type06.data[i]); + nvkm_wr32(device, 0x10f918 + j, train->type00.data[i]); + nvkm_wr32(device, 0x10f920 + j, 0x00000100 | + train->type09.data[i] << 4 | + train->type07.data[i]); + nvkm_wr32(device, 0x10f918 + j, train->type01.data[i]); + } + } + + for (j = 0; j < 8; j += 4) { + for (i = 0; i < 0x100; i++) { + nvkm_wr32(device, 0x10f968 + j, i); + nvkm_wr32(device, 0x10f900 + j, train->type04.data[i]); + } + } + + return 0; +} + +static int +gk104_ram_train_init(struct nvkm_ram *ram) +{ + u8 ramcfg = nvbios_ramcfg_index(&ram->fb->subdev); + struct gk104_ram_train *train; + int ret, i; + + if (!(train = kzalloc(sizeof(*train), GFP_KERNEL))) + return -ENOMEM; + + for (i = 0; i < 0x100; i++) { + ret = gk104_ram_train_type(ram, i, ramcfg, train); + if (ret && ret != -ENOENT) + break; + } + + switch (ram->type) { + case NVKM_RAM_TYPE_GDDR5: + ret = gk104_ram_train_init_0(ram, train); + break; + default: + ret = 0; + break; + } + + kfree(train); + return ret; +} + +int +gk104_ram_init(struct nvkm_ram *ram) +{ + struct nvkm_subdev *subdev = &ram->fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + u8 ver, hdr, cnt, len, snr, ssz; + u32 data, save; + int i; + + /* run a bunch of tables from rammap table. there's actually + * individual pointers for each rammap entry too, but, nvidia + * seem to just run the last two entries' scripts early on in + * their init, and never again.. we'll just run 'em all once + * for now. + * + * i strongly suspect that each script is for a separate mode + * (likely selected by 0x10f65c's lower bits?), and the + * binary driver skips the one that's already been setup by + * the init tables. + */ + data = nvbios_rammapTe(bios, &ver, &hdr, &cnt, &len, &snr, &ssz); + if (!data || hdr < 0x15) + return -EINVAL; + + cnt = nvbios_rd08(bios, data + 0x14); /* guess at count */ + data = nvbios_rd32(bios, data + 0x10); /* guess u32... */ + save = nvkm_rd32(device, 0x10f65c) & 0x000000f0; + for (i = 0; i < cnt; i++, data += 4) { + if (i != save >> 4) { + nvkm_mask(device, 0x10f65c, 0x000000f0, i << 4); + nvbios_init(subdev, nvbios_rd32(bios, data)); + } + } + nvkm_mask(device, 0x10f65c, 0x000000f0, save); + nvkm_mask(device, 0x10f584, 0x11000000, 0x00000000); + nvkm_wr32(device, 0x10ecc0, 0xffffffff); + nvkm_mask(device, 0x10f160, 0x00000010, 0x00000010); + + return gk104_ram_train_init(ram); +} + +static int +gk104_ram_ctor_data(struct gk104_ram *ram, u8 ramcfg, int i) +{ + struct nvkm_bios *bios = ram->base.fb->subdev.device->bios; + struct nvkm_ram_data *cfg; + struct nvbios_ramcfg *d = &ram->diff; + struct nvbios_ramcfg *p, *n; + u8 ver, hdr, cnt, len; + u32 data; + int ret; + + if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL))) + return -ENOMEM; + p = &list_last_entry(&ram->cfg, typeof(*cfg), head)->bios; + n = &cfg->bios; + + /* memory config data for a range of target frequencies */ + data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios); + if (ret = -ENOENT, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12) + goto done; + + /* ... and a portion specific to the attached memory */ + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg, + &ver, &hdr, &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a) + goto done; + + /* lookup memory timings, if bios says they're present */ + if (cfg->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33) + goto done; + } + + list_add_tail(&cfg->head, &ram->cfg); + if (ret = 0, i == 0) + goto done; + + d->rammap_11_0a_03fe |= p->rammap_11_0a_03fe != n->rammap_11_0a_03fe; + d->rammap_11_09_01ff |= p->rammap_11_09_01ff != n->rammap_11_09_01ff; + d->rammap_11_0a_0400 |= p->rammap_11_0a_0400 != n->rammap_11_0a_0400; + d->rammap_11_0a_0800 |= p->rammap_11_0a_0800 != n->rammap_11_0a_0800; + d->rammap_11_0b_01f0 |= p->rammap_11_0b_01f0 != n->rammap_11_0b_01f0; + d->rammap_11_0b_0200 |= p->rammap_11_0b_0200 != n->rammap_11_0b_0200; + d->rammap_11_0d |= p->rammap_11_0d != n->rammap_11_0d; + d->rammap_11_0f |= p->rammap_11_0f != n->rammap_11_0f; + d->rammap_11_0e |= p->rammap_11_0e != n->rammap_11_0e; + d->rammap_11_0b_0800 |= p->rammap_11_0b_0800 != n->rammap_11_0b_0800; + d->rammap_11_0b_0400 |= p->rammap_11_0b_0400 != n->rammap_11_0b_0400; + d->ramcfg_11_01_01 |= p->ramcfg_11_01_01 != n->ramcfg_11_01_01; + d->ramcfg_11_01_02 |= p->ramcfg_11_01_02 != n->ramcfg_11_01_02; + d->ramcfg_11_01_10 |= p->ramcfg_11_01_10 != n->ramcfg_11_01_10; + d->ramcfg_11_02_03 |= p->ramcfg_11_02_03 != n->ramcfg_11_02_03; + d->ramcfg_11_08_20 |= p->ramcfg_11_08_20 != n->ramcfg_11_08_20; + d->timing_20_30_07 |= p->timing_20_30_07 != n->timing_20_30_07; +done: + if (ret) + kfree(cfg); + return ret; +} + +void * +gk104_ram_dtor(struct nvkm_ram *base) +{ + struct gk104_ram *ram = gk104_ram(base); + struct nvkm_ram_data *cfg, *tmp; + + list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) { + kfree(cfg); + } + + return ram; +} + +int +gk104_ram_new_(const struct nvkm_ram_func *func, struct nvkm_fb *fb, + struct nvkm_ram **pram) +{ + struct nvkm_subdev *subdev = &fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct dcb_gpio_func gpio; + struct gk104_ram *ram; + int ret, i; + u8 ramcfg = nvbios_ramcfg_index(subdev); + u32 tmp; + + if (!(ram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + *pram = &ram->base; + + ret = gf100_ram_ctor(func, fb, &ram->base); + if (ret) + return ret; + + INIT_LIST_HEAD(&ram->cfg); + + /* calculate a mask of differently configured memory partitions, + * because, of course reclocking wasn't complicated enough + * already without having to treat some of them differently to + * the others.... + */ + ram->parts = nvkm_rd32(device, 0x022438); + ram->pmask = nvkm_rd32(device, 0x022554); + ram->pnuts = 0; + for (i = 0, tmp = 0; i < ram->parts; i++) { + if (!(ram->pmask & (1 << i))) { + u32 cfg1 = nvkm_rd32(device, 0x110204 + (i * 0x1000)); + if (tmp && tmp != cfg1) { + ram->pnuts |= (1 << i); + continue; + } + tmp = cfg1; + } + } + + /* parse bios data for all rammap table entries up-front, and + * build information on whether certain fields differ between + * any of the entries. + * + * the binary driver appears to completely ignore some fields + * when all entries contain the same value. at first, it was + * hoped that these were mere optimisations and the bios init + * tables had configured as per the values here, but there is + * evidence now to suggest that this isn't the case and we do + * need to treat this condition as a "don't touch" indicator. + */ + for (i = 0; !ret; i++) { + ret = gk104_ram_ctor_data(ram, ramcfg, i); + if (ret && ret != -ENOENT) { + nvkm_error(subdev, "failed to parse ramcfg data\n"); + return ret; + } + } + + /* parse bios data for both pll's */ + ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll); + if (ret) { + nvkm_error(subdev, "mclk refpll data not found\n"); + return ret; + } + + ret = nvbios_pll_parse(bios, 0x04, &ram->fuc.mempll); + if (ret) { + nvkm_error(subdev, "mclk pll data not found\n"); + return ret; + } + + /* lookup memory voltage gpios */ + ret = nvkm_gpio_find(device->gpio, 0, 0x18, DCB_GPIO_UNUSED, &gpio); + if (ret == 0) { + ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (gpio.line * 0x04)); + ram->fuc.r_funcMV[0] = (gpio.log[0] ^ 2) << 12; + ram->fuc.r_funcMV[1] = (gpio.log[1] ^ 2) << 12; + } + + ret = nvkm_gpio_find(device->gpio, 0, 0x2e, DCB_GPIO_UNUSED, &gpio); + if (ret == 0) { + ram->fuc.r_gpio2E = ramfuc_reg(0x00d610 + (gpio.line * 0x04)); + ram->fuc.r_func2E[0] = (gpio.log[0] ^ 2) << 12; + ram->fuc.r_func2E[1] = (gpio.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 NVKM_RAM_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 NVKM_RAM_TYPE_DDR3: + ram->fuc.r_mr[0] = ramfuc_reg(0x10f300); + ram->fuc.r_mr[1] = ramfuc_reg(0x10f304); + ram->fuc.r_mr[2] = ramfuc_reg(0x10f320); + break; + default: + break; + } + + ram->fuc.r_0x62c000 = ramfuc_reg(0x62c000); + ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200); + ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210); + ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310); + ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314); + ram->fuc.r_0x10f318 = ramfuc_reg(0x10f318); + ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090); + ram->fuc.r_0x10f69c = ramfuc_reg(0x10f69c); + ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824); + ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0); + ram->fuc.r_0x1373f4 = ramfuc_reg(0x1373f4); + ram->fuc.r_0x137320 = ramfuc_reg(0x137320); + ram->fuc.r_0x10f65c = ramfuc_reg(0x10f65c); + ram->fuc.r_0x10f6bc = ramfuc_reg(0x10f6bc); + ram->fuc.r_0x100710 = ramfuc_reg(0x100710); + ram->fuc.r_0x100750 = ramfuc_reg(0x100750); + return 0; +} + +static const struct nvkm_ram_func +gk104_ram = { + .upper = 0x0200000000ULL, + .probe_fbp = gf100_ram_probe_fbp, + .probe_fbp_amount = gf108_ram_probe_fbp_amount, + .probe_fbpa_amount = gf100_ram_probe_fbpa_amount, + .dtor = gk104_ram_dtor, + .init = gk104_ram_init, + .calc = gk104_ram_calc, + .prog = gk104_ram_prog, + .tidy = gk104_ram_tidy, +}; + +int +gk104_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + return gk104_ram_new_(&gk104_ram, fb, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c new file mode 100644 index 000000000..be91da854 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c @@ -0,0 +1,51 @@ +/* + * 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 "ram.h" + +u32 +gm107_ram_probe_fbp(const struct nvkm_ram_func *func, + struct nvkm_device *device, int fbp, int *pltcs) +{ + u32 fbpao = nvkm_rd32(device, 0x021c14); + return func->probe_fbp_amount(func, fbpao, device, fbp, pltcs); +} + +static const struct nvkm_ram_func +gm107_ram = { + .upper = 0x1000000000ULL, + .probe_fbp = gm107_ram_probe_fbp, + .probe_fbp_amount = gf108_ram_probe_fbp_amount, + .probe_fbpa_amount = gf100_ram_probe_fbpa_amount, + .dtor = gk104_ram_dtor, + .init = gk104_ram_init, + .calc = gk104_ram_calc, + .prog = gk104_ram_prog, + .tidy = gk104_ram_tidy, +}; + +int +gm107_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + return gk104_ram_new_(&gm107_ram, fb, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm200.c new file mode 100644 index 000000000..8f91ea91e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm200.c @@ -0,0 +1,66 @@ +/* + * Copyright 2017 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 "ram.h" + +u32 +gm200_ram_probe_fbp_amount(const struct nvkm_ram_func *func, u32 fbpao, + struct nvkm_device *device, int fbp, int *pltcs) +{ + u32 ltcs = nvkm_rd32(device, 0x022450); + u32 fbpas = nvkm_rd32(device, 0x022458); + u32 fbpa = fbp * fbpas; + u32 size = 0; + if (!(nvkm_rd32(device, 0x021d38) & BIT(fbp))) { + u32 ltco = nvkm_rd32(device, 0x021d70 + (fbp * 4)); + u32 ltcm = ~ltco & ((1 << ltcs) - 1); + + while (fbpas--) { + if (!(fbpao & (1 << fbpa))) + size += func->probe_fbpa_amount(device, fbpa); + fbpa++; + } + + *pltcs = hweight32(ltcm); + } + return size; +} + +static const struct nvkm_ram_func +gm200_ram = { + .upper = 0x1000000000ULL, + .probe_fbp = gm107_ram_probe_fbp, + .probe_fbp_amount = gm200_ram_probe_fbp_amount, + .probe_fbpa_amount = gf100_ram_probe_fbpa_amount, + .dtor = gk104_ram_dtor, + .init = gk104_ram_init, + .calc = gk104_ram_calc, + .prog = gk104_ram_prog, + .tidy = gk104_ram_tidy, +}; + +int +gm200_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + return gk104_ram_new_(&gm200_ram, fb, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c new file mode 100644 index 000000000..378f6fb70 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.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 + */ +#include "ram.h" + +#include +#include +#include + +static int +gp100_ram_init(struct nvkm_ram *ram) +{ + struct nvkm_subdev *subdev = &ram->fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + u8 ver, hdr, cnt, len, snr, ssz; + u32 data; + int i; + + /* 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 0x9a065c's lower bits?), and the + * binary driver skips the one that's already been setup by + * the init tables. + */ + data = nvbios_rammapTe(bios, &ver, &hdr, &cnt, &len, &snr, &ssz); + if (!data || hdr < 0x15) + return -EINVAL; + + cnt = nvbios_rd08(bios, data + 0x14); /* guess at count */ + data = nvbios_rd32(bios, data + 0x10); /* guess u32... */ + if (cnt) { + u32 save = nvkm_rd32(device, 0x9a065c) & 0x000000f0; + for (i = 0; i < cnt; i++, data += 4) { + if (i != save >> 4) { + nvkm_mask(device, 0x9a065c, 0x000000f0, i << 4); + nvbios_init(subdev, nvbios_rd32(bios, data)); + } + } + nvkm_mask(device, 0x9a065c, 0x000000f0, save); + } + + nvkm_mask(device, 0x9a0584, 0x11000000, 0x00000000); + nvkm_wr32(device, 0x10ecc0, 0xffffffff); + nvkm_mask(device, 0x9a0160, 0x00000010, 0x00000010); + return 0; +} + +static u32 +gp100_ram_probe_fbpa(struct nvkm_device *device, int fbpa) +{ + return nvkm_rd32(device, 0x90020c + (fbpa * 0x4000)); +} + +static const struct nvkm_ram_func +gp100_ram = { + .upper = 0x1000000000ULL, + .probe_fbp = gm107_ram_probe_fbp, + .probe_fbp_amount = gm200_ram_probe_fbp_amount, + .probe_fbpa_amount = gp100_ram_probe_fbpa, + .init = gp100_ram_init, +}; + +int +gp100_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_ram *ram; + + if (!(ram = *pram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + + return gf100_ram_ctor(&gp100_ram, fb, ram); + +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c new file mode 100644 index 000000000..bbfde1cb3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c @@ -0,0 +1,1007 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Roy Spliet + */ +#define gt215_ram(p) container_of((p), struct gt215_ram, base) +#include "ram.h" +#include "ramfuc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct gt215_ramfuc { + struct ramfuc base; + struct ramfuc_reg r_0x001610; + struct ramfuc_reg r_0x001700; + struct ramfuc_reg r_0x002504; + struct ramfuc_reg r_0x004000; + struct ramfuc_reg r_0x004004; + struct ramfuc_reg r_0x004018; + struct ramfuc_reg r_0x004128; + struct ramfuc_reg r_0x004168; + struct ramfuc_reg r_0x100080; + struct ramfuc_reg r_0x100200; + struct ramfuc_reg r_0x100210; + struct ramfuc_reg r_0x100220[9]; + struct ramfuc_reg r_0x100264; + struct ramfuc_reg r_0x1002d0; + struct ramfuc_reg r_0x1002d4; + struct ramfuc_reg r_0x1002dc; + struct ramfuc_reg r_0x10053c; + struct ramfuc_reg r_0x1005a0; + struct ramfuc_reg r_0x1005a4; + struct ramfuc_reg r_0x100700; + struct ramfuc_reg r_0x100714; + struct ramfuc_reg r_0x100718; + struct ramfuc_reg r_0x10071c; + struct ramfuc_reg r_0x100720; + struct ramfuc_reg r_0x100760; + struct ramfuc_reg r_0x1007a0; + struct ramfuc_reg r_0x1007e0; + struct ramfuc_reg r_0x100da0; + struct ramfuc_reg r_0x10f804; + struct ramfuc_reg r_0x1110e0; + struct ramfuc_reg r_0x111100; + struct ramfuc_reg r_0x111104; + struct ramfuc_reg r_0x1111e0; + struct ramfuc_reg r_0x111400; + struct ramfuc_reg r_0x611200; + struct ramfuc_reg r_mr[4]; + struct ramfuc_reg r_gpio[4]; +}; + +struct gt215_ltrain { + enum { + NVA3_TRAIN_UNKNOWN, + NVA3_TRAIN_UNSUPPORTED, + NVA3_TRAIN_ONCE, + NVA3_TRAIN_EXEC, + NVA3_TRAIN_DONE + } state; + u32 r_100720; + u32 r_1111e0; + u32 r_111400; + struct nvkm_memory *memory; +}; + +struct gt215_ram { + struct nvkm_ram base; + struct gt215_ramfuc fuc; + struct gt215_ltrain ltrain; +}; + +static void +gt215_link_train_calc(u32 *vals, struct gt215_ltrain *train) +{ + int i, lo, hi; + u8 median[8], bins[4] = {0, 0, 0, 0}, bin = 0, qty = 0; + + for (i = 0; i < 8; i++) { + for (lo = 0; lo < 0x40; lo++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (vals[lo] & (0x101 << i)) + break; + } + + if (lo == 0x40) + return; + + for (hi = lo + 1; hi < 0x40; hi++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (!(vals[hi] & (0x101 << i))) { + hi--; + break; + } + } + + median[i] = ((hi - lo) >> 1) + lo; + bins[(median[i] & 0xf0) >> 4]++; + median[i] += 0x30; + } + + /* Find the best value for 0x1111e0 */ + for (i = 0; i < 4; i++) { + if (bins[i] > qty) { + bin = i + 3; + qty = bins[i]; + } + } + + train->r_100720 = 0; + for (i = 0; i < 8; i++) { + median[i] = max(median[i], (u8) (bin << 4)); + median[i] = min(median[i], (u8) ((bin << 4) | 0xf)); + + train->r_100720 |= ((median[i] & 0x0f) << (i << 2)); + } + + train->r_1111e0 = 0x02000000 | (bin * 0x101); + train->r_111400 = 0x0; +} + +/* + * Link training for (at least) DDR3 + */ +static int +gt215_link_train(struct gt215_ram *ram) +{ + struct gt215_ltrain *train = &ram->ltrain; + struct gt215_ramfuc *fuc = &ram->fuc; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvkm_clk *clk = device->clk; + u32 *result, r1700; + int ret, i; + struct nvbios_M0205T M0205T = { 0 }; + u8 ver, hdr, cnt, len, snr, ssz; + unsigned int clk_current; + unsigned long flags; + unsigned long *f = &flags; + + if (nvkm_boolopt(device->cfgopt, "NvMemExec", true) != true) + return -ENOSYS; + + /* XXX: Multiple partitions? */ + result = kmalloc_array(64, sizeof(u32), GFP_KERNEL); + if (!result) + return -ENOMEM; + + train->state = NVA3_TRAIN_EXEC; + + /* Clock speeds for training and back */ + nvbios_M0205Tp(bios, &ver, &hdr, &cnt, &len, &snr, &ssz, &M0205T); + if (M0205T.freq == 0) { + kfree(result); + return -ENOENT; + } + + clk_current = nvkm_clk_read(clk, nv_clk_src_mem); + + ret = gt215_clk_pre(clk, f); + if (ret) + goto out; + + /* First: clock up/down */ + ret = ram->base.func->calc(&ram->base, (u32) M0205T.freq * 1000); + if (ret) + goto out; + + /* Do this *after* calc, eliminates write in script */ + nvkm_wr32(device, 0x111400, 0x00000000); + /* XXX: Magic writes that improve train reliability? */ + nvkm_mask(device, 0x100674, 0x0000ffff, 0x00000000); + nvkm_mask(device, 0x1005e4, 0x0000ffff, 0x00000000); + nvkm_mask(device, 0x100b0c, 0x000000ff, 0x00000000); + nvkm_wr32(device, 0x100c04, 0x00000400); + + /* Now the training script */ + r1700 = ram_rd32(fuc, 0x001700); + + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x611200, 0x3300); + ram_wait_vblank(fuc); + ram_wait(fuc, 0x611200, 0x00000003, 0x00000000, 500000); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000003); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_wr32(fuc, 0x001700, 0x00000000); + + ram_train(fuc); + + /* Reset */ + ram_mask(fuc, 0x10f804, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10053c, 0x0); + ram_wr32(fuc, 0x100720, train->r_100720); + ram_wr32(fuc, 0x1111e0, train->r_1111e0); + ram_wr32(fuc, 0x111400, train->r_111400); + ram_nuke(fuc, 0x100080); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000020); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x001700, r1700); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000080); + ram_wr32(fuc, 0x611200, 0x3330); + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + + ram_exec(fuc, true); + + ram->base.func->calc(&ram->base, clk_current); + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nvkm_mask(device, 0x616308, 0x10, 0x10); + nvkm_mask(device, 0x616b08, 0x10, 0x10); + + gt215_clk_post(clk, f); + + ram_train_result(ram->base.fb, result, 64); + for (i = 0; i < 64; i++) + nvkm_debug(subdev, "Train: %08x", result[i]); + gt215_link_train_calc(result, train); + + nvkm_debug(subdev, "Train: %08x %08x %08x", train->r_100720, + train->r_1111e0, train->r_111400); + + kfree(result); + + train->state = NVA3_TRAIN_DONE; + + return ret; + +out: + if(ret == -EBUSY) + f = NULL; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + gt215_clk_post(clk, f); + kfree(result); + return ret; +} + +static int +gt215_link_train_init(struct gt215_ram *ram) +{ + static const u32 pattern[16] = { + 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee, + 0x00000000, 0x11111111, 0x44444444, 0xdddddddd, + 0x33333333, 0x55555555, 0x77777777, 0x66666666, + 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb, + }; + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_device *device = ram->base.fb->subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvbios_M0205E M0205E; + u8 ver, hdr, cnt, len; + u32 r001700; + u64 addr; + int ret, i = 0; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + /* We support type "5" + * XXX: training pattern table appears to be unused for this routine */ + if (!nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)) + return -ENOENT; + + if (M0205E.type != 5) + return 0; + + train->state = NVA3_TRAIN_ONCE; + + ret = nvkm_ram_get(device, NVKM_RAM_MM_NORMAL, 0x01, 16, 0x8000, + true, true, &ram->ltrain.memory); + if (ret) + return ret; + + addr = nvkm_memory_addr(ram->ltrain.memory); + + nvkm_wr32(device, 0x100538, 0x10000000 | (addr >> 16)); + nvkm_wr32(device, 0x1005a8, 0x0000ffff); + nvkm_mask(device, 0x10f800, 0x00000001, 0x00000001); + + for (i = 0; i < 0x30; i++) { + nvkm_wr32(device, 0x10f8c0, (i << 8) | i); + nvkm_wr32(device, 0x10f900, pattern[i % 16]); + } + + for (i = 0; i < 0x30; i++) { + nvkm_wr32(device, 0x10f8e0, (i << 8) | i); + nvkm_wr32(device, 0x10f920, pattern[i % 16]); + } + + /* And upload the pattern */ + r001700 = nvkm_rd32(device, 0x1700); + nvkm_wr32(device, 0x1700, addr >> 16); + for (i = 0; i < 16; i++) + nvkm_wr32(device, 0x700000 + (i << 2), pattern[i]); + for (i = 0; i < 16; i++) + nvkm_wr32(device, 0x700100 + (i << 2), pattern[i]); + nvkm_wr32(device, 0x1700, r001700); + + train->r_100720 = nvkm_rd32(device, 0x100720); + train->r_1111e0 = nvkm_rd32(device, 0x1111e0); + train->r_111400 = nvkm_rd32(device, 0x111400); + return 0; +} + +static void +gt215_link_train_fini(struct gt215_ram *ram) +{ + nvkm_memory_unref(&ram->ltrain.memory); +} + +/* + * RAM reclocking + */ +#define T(t) cfg->timing_10_##t +static int +gt215_ram_timing_calc(struct gt215_ram *ram, u32 *timing) +{ + struct nvbios_ramcfg *cfg = &ram->base.target.bios; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + int tUNK_base, tUNK_40_0, prevCL; + u32 cur2, cur3, cur7, cur8; + + cur2 = nvkm_rd32(device, 0x100228); + cur3 = nvkm_rd32(device, 0x10022c); + cur7 = nvkm_rd32(device, 0x10023c); + cur8 = nvkm_rd32(device, 0x100240); + + + switch ((!T(CWL)) * ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + T(CWL) = T(CL) - 1; + break; + case NVKM_RAM_TYPE_GDDR3: + T(CWL) = ((cur2 & 0xff000000) >> 24) + 1; + break; + } + + prevCL = (cur3 & 0x000000ff) + 1; + tUNK_base = ((cur7 & 0x00ff0000) >> 16) - prevCL; + + timing[0] = (T(RP) << 24 | T(RAS) << 16 | T(RFC) << 8 | T(RC)); + timing[1] = (T(WR) + 1 + T(CWL)) << 24 | + max_t(u8,T(18), 1) << 16 | + (T(WTR) + 1 + T(CWL)) << 8 | + (5 + T(CL) - T(CWL)); + timing[2] = (T(CWL) - 1) << 24 | + (T(RRD) << 16) | + (T(RCDWR) << 8) | + T(RCDRD); + timing[3] = (cur3 & 0x00ff0000) | + (0x30 + T(CL)) << 24 | + (0xb + T(CL)) << 8 | + (T(CL) - 1); + timing[4] = T(20) << 24 | + T(21) << 16 | + T(13) << 8 | + T(13); + timing[5] = T(RFC) << 24 | + max_t(u8,T(RCDRD), T(RCDWR)) << 16 | + max_t(u8, (T(CWL) + 6), (T(CL) + 2)) << 8 | + T(RP); + timing[6] = (0x5a + T(CL)) << 16 | + max_t(u8, 1, (6 - T(CL) + T(CWL))) << 8 | + (0x50 + T(CL) - T(CWL)); + timing[7] = (cur7 & 0xff000000) | + ((tUNK_base + T(CL)) << 16) | + 0x202; + timing[8] = cur8 & 0xffffff00; + + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + case NVKM_RAM_TYPE_GDDR3: + tUNK_40_0 = prevCL - (cur8 & 0xff); + if (tUNK_40_0 > 0) + timing[8] |= T(CL); + break; + default: + break; + } + + nvkm_debug(subdev, "Entry: 220: %08x %08x %08x %08x\n", + timing[0], timing[1], timing[2], timing[3]); + nvkm_debug(subdev, " 230: %08x %08x %08x %08x\n", + timing[4], timing[5], timing[6], timing[7]); + nvkm_debug(subdev, " 240: %08x\n", timing[8]); + return 0; +} +#undef T + +static void +nvkm_sddr2_dll_reset(struct gt215_ramfuc *fuc) +{ + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[0], 0x100, 0x000); + ram_nsec(fuc, 1000); +} + +static void +nvkm_sddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x1)) { + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +nvkm_gddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x40)) { + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +gt215_ram_lock_pll(struct gt215_ramfuc *fuc, struct gt215_clk_info *mclk) +{ + ram_wr32(fuc, 0x004004, mclk->pll); + ram_mask(fuc, 0x004000, 0x00000001, 0x00000001); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000000); + ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000010); +} + +static void +gt215_ram_gpio(struct gt215_ramfuc *fuc, u8 tag, u32 val) +{ + struct nvkm_gpio *gpio = fuc->base.fb->subdev.device->gpio; + struct dcb_gpio_func func; + u32 reg, sh, gpio_val; + int ret; + + if (nvkm_gpio_get(gpio, 0, tag, DCB_GPIO_UNUSED) != val) { + ret = nvkm_gpio_find(gpio, 0, tag, DCB_GPIO_UNUSED, &func); + if (ret) + return; + + reg = func.line >> 3; + sh = (func.line & 0x7) << 2; + gpio_val = ram_rd32(fuc, gpio[reg]); + if (gpio_val & (8 << sh)) + val = !val; + if (!(func.log[1] & 1)) + val = !val; + + ram_mask(fuc, gpio[reg], (0x3 << sh), ((val | 0x2) << sh)); + ram_nsec(fuc, 20000); + } +} + +static int +gt215_ram_calc(struct nvkm_ram *base, u32 freq) +{ + struct gt215_ram *ram = gt215_ram(base); + struct gt215_ramfuc *fuc = &ram->fuc; + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct gt215_clk_info mclk; + struct nvkm_gpio *gpio = device->gpio; + struct nvkm_ram_data *next; + u8 ver, hdr, cnt, len, strap; + u32 data; + u32 r004018, r100760, r100da0, r111100, ctrl; + u32 unk714, unk718, unk71c; + int ret, i; + u32 timing[9]; + bool pll2pll; + + next = &ram->base.target; + next->freq = freq; + ram->base.next = next; + + if (ram->ltrain.state == NVA3_TRAIN_ONCE) + gt215_link_train(ram); + + /* lookup memory config data relevant to the target frequency */ + data = nvbios_rammapEm(bios, freq / 1000, &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x05) { + nvkm_error(subdev, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(subdev); + if (strap >= cnt) { + nvkm_error(subdev, "invalid ramcfg strap\n"); + return -EINVAL; + } + + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap, + &ver, &hdr, &next->bios); + if (!data || ver != 0x10 || hdr < 0x09) { + nvkm_error(subdev, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + if (next->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, next->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x17) { + nvkm_error(subdev, "invalid/missing timing entry\n"); + return -EINVAL; + } + } + + ret = gt215_pll_info(device->clk, 0x12, 0x4000, freq, &mclk); + if (ret < 0) { + nvkm_error(subdev, "failed mclk calculation\n"); + return ret; + } + + gt215_ram_timing_calc(ram, timing); + + ret = ram_init(fuc, ram->base.fb); + if (ret) + return ret; + + /* Determine ram-specific MR values */ + ram->base.mr[0] = ram_rd32(fuc, mr[0]); + ram->base.mr[1] = ram_rd32(fuc, mr[1]); + ram->base.mr[2] = ram_rd32(fuc, mr[2]); + + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + ret = nvkm_sddr2_calc(&ram->base); + break; + case NVKM_RAM_TYPE_DDR3: + ret = nvkm_sddr3_calc(&ram->base); + break; + case NVKM_RAM_TYPE_GDDR3: + ret = nvkm_gddr3_calc(&ram->base); + break; + default: + ret = -ENOSYS; + break; + } + + if (ret) + return ret; + + /* XXX: 750MHz seems rather arbitrary */ + if (freq <= 750000) { + r004018 = 0x10000000; + r100760 = 0x22222222; + r100da0 = 0x00000010; + } else { + r004018 = 0x00000000; + r100760 = 0x00000000; + r100da0 = 0x00000000; + } + + if (!next->bios.ramcfg_DLLoff) + r004018 |= 0x00004000; + + /* pll2pll requires to switch to a safe clock first */ + ctrl = ram_rd32(fuc, 0x004000); + pll2pll = (!(ctrl & 0x00000008)) && mclk.pll; + + /* Pre, NVIDIA does this outside the script */ + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); + } else { + ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); + ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); + } + /* Always disable this bit during reclock */ + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + + /* If switching from non-pll to pll, lock before disabling FB */ + if (mclk.pll && !pll2pll) { + ram_mask(fuc, 0x004128, 0x003f3141, mclk.clk | 0x00000101); + gt215_ram_lock_pll(fuc, &mclk); + } + + /* Start with disabling some CRTCs and PFIFO? */ + ram_wait_vblank(fuc); + ram_wr32(fuc, 0x611200, 0x3300); + ram_mask(fuc, 0x002504, 0x1, 0x1); + ram_nsec(fuc, 10000); + ram_wait(fuc, 0x002504, 0x10, 0x10, 20000); /* XXX: or longer? */ + ram_block(fuc); + ram_nsec(fuc, 2000); + + if (!next->bios.ramcfg_10_02_10) { + if (ram->base.type == NVKM_RAM_TYPE_GDDR3) + ram_mask(fuc, 0x111100, 0x04020000, 0x00020000); + else + ram_mask(fuc, 0x111100, 0x04020000, 0x04020000); + } + + /* If we're disabling the DLL, do it now */ + switch (next->bios.ramcfg_DLLoff * ram->base.type) { + case NVKM_RAM_TYPE_DDR3: + nvkm_sddr3_dll_disable(fuc, ram->base.mr); + break; + case NVKM_RAM_TYPE_GDDR3: + nvkm_gddr3_dll_disable(fuc, ram->base.mr); + break; + } + + if (next->bios.timing_10_ODT) + gt215_ram_gpio(fuc, 0x2e, 1); + + /* Brace RAM for impact */ + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x100210, 0x00000000); + ram_wr32(fuc, 0x1002dc, 0x00000001); + ram_nsec(fuc, 2000); + + if (device->chipset == 0xa3 && freq <= 500000) + ram_mask(fuc, 0x100700, 0x00000006, 0x00000006); + + /* Alter FBVDD/Q, apparently must be done with PLL disabled, thus + * set it to bypass */ + if (nvkm_gpio_get(gpio, 0, 0x18, DCB_GPIO_UNUSED) == + next->bios.ramcfg_FBVDDQ) { + data = ram_rd32(fuc, 0x004000) & 0x9; + + if (data == 0x1) + ram_mask(fuc, 0x004000, 0x8, 0x8); + if (data & 0x1) + ram_mask(fuc, 0x004000, 0x1, 0x0); + + gt215_ram_gpio(fuc, 0x18, !next->bios.ramcfg_FBVDDQ); + + if (data & 0x1) + ram_mask(fuc, 0x004000, 0x1, 0x1); + } + + /* Fiddle with clocks */ + /* There's 4 scenario's + * pll->pll: first switch to a 324MHz clock, set up new PLL, switch + * clk->pll: Set up new PLL, switch + * pll->clk: Set up clock, switch + * clk->clk: Overwrite ctrl and other bits, switch */ + + /* Switch to regular clock - 324MHz */ + if (pll2pll) { + ram_mask(fuc, 0x004000, 0x00000004, 0x00000004); + ram_mask(fuc, 0x004168, 0x003f3141, 0x00083101); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00001000); + gt215_ram_lock_pll(fuc, &mclk); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004000, 0x00000105, 0x00000105); + ram_wr32(fuc, 0x004018, 0x00001000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } else { + ram_mask(fuc, 0x004168, 0x003f3141, mclk.clk | 0x00000101); + ram_mask(fuc, 0x004000, 0x00000108, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00009000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } + ram_nsec(fuc, 20000); + + if (next->bios.rammap_10_04_08) { + ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 | + next->bios.ramcfg_10_05 << 8 | + next->bios.ramcfg_10_05); + ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 | + next->bios.ramcfg_10_07); + ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 | + next->bios.ramcfg_10_03_0f << 16 | + next->bios.ramcfg_10_09_0f | + 0x80000000); + ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); + } else { + if (train->state == NVA3_TRAIN_DONE) { + ram_wr32(fuc, 0x100080, 0x1020); + ram_mask(fuc, 0x111400, 0xffffffff, train->r_111400); + ram_mask(fuc, 0x1111e0, 0xffffffff, train->r_1111e0); + ram_mask(fuc, 0x100720, 0xffffffff, train->r_100720); + } + ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_mask(fuc, 0x100760, 0x22222222, r100760); + ram_mask(fuc, 0x1007a0, 0x22222222, r100760); + ram_mask(fuc, 0x1007e0, 0x22222222, r100760); + } + + if (device->chipset == 0xa3 && freq > 500000) { + ram_mask(fuc, 0x100700, 0x00000006, 0x00000000); + } + + /* Final switch */ + if (mclk.pll) { + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000000); + } + + ram_wr32(fuc, 0x1002dc, 0x00000000); + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x100210, 0x80000000); + ram_nsec(fuc, 2000); + + /* Set RAM MR parameters and timings */ + for (i = 2; i >= 0; i--) { + if (ram_rd32(fuc, mr[i]) != ram->base.mr[i]) { + ram_wr32(fuc, mr[i], ram->base.mr[i]); + ram_nsec(fuc, 1000); + } + } + + ram_wr32(fuc, 0x100220[3], timing[3]); + ram_wr32(fuc, 0x100220[1], timing[1]); + ram_wr32(fuc, 0x100220[6], timing[6]); + ram_wr32(fuc, 0x100220[7], timing[7]); + ram_wr32(fuc, 0x100220[2], timing[2]); + ram_wr32(fuc, 0x100220[4], timing[4]); + ram_wr32(fuc, 0x100220[5], timing[5]); + ram_wr32(fuc, 0x100220[0], timing[0]); + ram_wr32(fuc, 0x100220[8], timing[8]); + + /* Misc */ + ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12); + + /* XXX: A lot of "chipset"/"ram type" specific stuff...? */ + unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000130; + unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; + unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; + r111100 = ram_rd32(fuc, 0x111100) & ~0x3a800000; + + /* NVA8 seems to skip various bits related to ramcfg_10_02_04 */ + if (device->chipset == 0xa8) { + r111100 |= 0x08000000; + if (!next->bios.ramcfg_10_02_04) + unk714 |= 0x00000010; + } else { + if (next->bios.ramcfg_10_02_04) { + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + case NVKM_RAM_TYPE_DDR3: + r111100 &= ~0x00000020; + if (next->bios.ramcfg_10_02_10) + r111100 |= 0x08000004; + else + r111100 |= 0x00000024; + break; + default: + break; + } + } else { + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + case NVKM_RAM_TYPE_DDR3: + r111100 &= ~0x00000024; + r111100 |= 0x12800000; + + if (next->bios.ramcfg_10_02_10) + r111100 |= 0x08000000; + unk714 |= 0x00000010; + break; + case NVKM_RAM_TYPE_GDDR3: + r111100 |= 0x30000000; + unk714 |= 0x00000020; + break; + default: + break; + } + } + } + + unk714 |= (next->bios.ramcfg_10_04_01) << 8; + + if (next->bios.ramcfg_10_02_20) + unk714 |= 0xf0000000; + if (next->bios.ramcfg_10_02_02) + unk718 |= 0x00000100; + if (next->bios.ramcfg_10_02_01) + unk71c |= 0x00000100; + if (next->bios.timing_10_24 != 0xff) { + unk718 &= ~0xf0000000; + unk718 |= next->bios.timing_10_24 << 28; + } + if (next->bios.ramcfg_10_02_10) + r111100 &= ~0x04020000; + + ram_mask(fuc, 0x100714, 0xffffffff, unk714); + ram_mask(fuc, 0x10071c, 0xffffffff, unk71c); + ram_mask(fuc, 0x100718, 0xffffffff, unk718); + ram_mask(fuc, 0x111100, 0xffffffff, r111100); + + if (!next->bios.timing_10_ODT) + gt215_ram_gpio(fuc, 0x2e, 0); + + /* Reset DLL */ + if (!next->bios.ramcfg_DLLoff) + nvkm_sddr2_dll_reset(fuc); + + if (ram->base.type == NVKM_RAM_TYPE_GDDR3) { + ram_nsec(fuc, 31000); + } else { + ram_nsec(fuc, 14000); + } + + if (ram->base.type == NVKM_RAM_TYPE_DDR3) { + ram_wr32(fuc, 0x100264, 0x1); + ram_nsec(fuc, 2000); + } + + ram_nuke(fuc, 0x100700); + ram_mask(fuc, 0x100700, 0x01000000, 0x01000000); + ram_mask(fuc, 0x100700, 0x01000000, 0x00000000); + + /* Re-enable FB */ + ram_unblock(fuc); + ram_wr32(fuc, 0x611200, 0x3330); + + /* Post fiddlings */ + if (next->bios.rammap_10_04_02) + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000180, 0x00000180); + ram_mask(fuc, 0x111100, 0x40000000, 0x00000000); + } else { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000600); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004168, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004168, 0x00000100, 0x00000000); + } else { + ram_mask(fuc, 0x004000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000100, 0x00000000); + } + + return 0; +} + +static int +gt215_ram_prog(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + struct gt215_ramfuc *fuc = &ram->fuc; + struct nvkm_device *device = ram->base.fb->subdev.device; + bool exec = nvkm_boolopt(device->cfgopt, "NvMemExec", true); + + if (exec) { + nvkm_mask(device, 0x001534, 0x2, 0x2); + + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nvkm_mask(device, 0x002504, 0x1, 0x0); + nvkm_mask(device, 0x001534, 0x2, 0x0); + + nvkm_mask(device, 0x616308, 0x10, 0x10); + nvkm_mask(device, 0x616b08, 0x10, 0x10); + } else { + ram_exec(fuc, false); + } + return 0; +} + +static void +gt215_ram_tidy(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + ram_exec(&ram->fuc, false); +} + +static int +gt215_ram_init(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + gt215_link_train_init(ram); + return 0; +} + +static void * +gt215_ram_dtor(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + gt215_link_train_fini(ram); + return ram; +} + +static const struct nvkm_ram_func +gt215_ram_func = { + .dtor = gt215_ram_dtor, + .init = gt215_ram_init, + .calc = gt215_ram_calc, + .prog = gt215_ram_prog, + .tidy = gt215_ram_tidy, +}; + +int +gt215_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct gt215_ram *ram; + int ret, i; + + if (!(ram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + *pram = &ram->base; + + ret = nv50_ram_ctor(>215_ram_func, fb, &ram->base); + if (ret) + return ret; + + ram->fuc.r_0x001610 = ramfuc_reg(0x001610); + ram->fuc.r_0x001700 = ramfuc_reg(0x001700); + ram->fuc.r_0x002504 = ramfuc_reg(0x002504); + ram->fuc.r_0x004000 = ramfuc_reg(0x004000); + ram->fuc.r_0x004004 = ramfuc_reg(0x004004); + ram->fuc.r_0x004018 = ramfuc_reg(0x004018); + ram->fuc.r_0x004128 = ramfuc_reg(0x004128); + ram->fuc.r_0x004168 = ramfuc_reg(0x004168); + ram->fuc.r_0x100080 = ramfuc_reg(0x100080); + ram->fuc.r_0x100200 = ramfuc_reg(0x100200); + ram->fuc.r_0x100210 = ramfuc_reg(0x100210); + for (i = 0; i < 9; i++) + ram->fuc.r_0x100220[i] = ramfuc_reg(0x100220 + (i * 4)); + ram->fuc.r_0x100264 = ramfuc_reg(0x100264); + ram->fuc.r_0x1002d0 = ramfuc_reg(0x1002d0); + ram->fuc.r_0x1002d4 = ramfuc_reg(0x1002d4); + ram->fuc.r_0x1002dc = ramfuc_reg(0x1002dc); + ram->fuc.r_0x10053c = ramfuc_reg(0x10053c); + ram->fuc.r_0x1005a0 = ramfuc_reg(0x1005a0); + ram->fuc.r_0x1005a4 = ramfuc_reg(0x1005a4); + ram->fuc.r_0x100700 = ramfuc_reg(0x100700); + ram->fuc.r_0x100714 = ramfuc_reg(0x100714); + ram->fuc.r_0x100718 = ramfuc_reg(0x100718); + ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); + ram->fuc.r_0x100720 = ramfuc_reg(0x100720); + ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask); + ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask); + ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask); + ram->fuc.r_0x100da0 = ramfuc_stride(0x100da0, 4, ram->base.part_mask); + ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); + ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask); + ram->fuc.r_0x111100 = ramfuc_reg(0x111100); + ram->fuc.r_0x111104 = ramfuc_reg(0x111104); + ram->fuc.r_0x1111e0 = ramfuc_reg(0x1111e0); + ram->fuc.r_0x111400 = ramfuc_reg(0x111400); + ram->fuc.r_0x611200 = ramfuc_reg(0x611200); + + if (ram->base.ranks > 1) { + ram->fuc.r_mr[0] = ramfuc_reg2(0x1002c0, 0x1002c8); + ram->fuc.r_mr[1] = ramfuc_reg2(0x1002c4, 0x1002cc); + ram->fuc.r_mr[2] = ramfuc_reg2(0x1002e0, 0x1002e8); + ram->fuc.r_mr[3] = ramfuc_reg2(0x1002e4, 0x1002ec); + } else { + ram->fuc.r_mr[0] = ramfuc_reg(0x1002c0); + ram->fuc.r_mr[1] = ramfuc_reg(0x1002c4); + ram->fuc.r_mr[2] = ramfuc_reg(0x1002e0); + ram->fuc.r_mr[3] = ramfuc_reg(0x1002e4); + } + ram->fuc.r_gpio[0] = ramfuc_reg(0x00e104); + ram->fuc.r_gpio[1] = ramfuc_reg(0x00e108); + ram->fuc.r_gpio[2] = ramfuc_reg(0x00e120); + ram->fuc.r_gpio[3] = ramfuc_reg(0x00e124); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c new file mode 100644 index 000000000..7de18e53e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c @@ -0,0 +1,86 @@ +/* + * 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 + */ +#define mcp77_ram(p) container_of((p), struct mcp77_ram, base) +#include "ram.h" + +struct mcp77_ram { + struct nvkm_ram base; + u64 poller_base; +}; + +static int +mcp77_ram_init(struct nvkm_ram *base) +{ + struct mcp77_ram *ram = mcp77_ram(base); + struct nvkm_device *device = ram->base.fb->subdev.device; + u32 dniso = ((ram->base.size - (ram->poller_base + 0x00)) >> 5) - 1; + u32 hostnb = ((ram->base.size - (ram->poller_base + 0x20)) >> 5) - 1; + u32 flush = ((ram->base.size - (ram->poller_base + 0x40)) >> 5) - 1; + + /* Enable NISO poller for various clients and set their associated + * read address, only for MCP77/78 and MCP79/7A. (fd#27501) + */ + nvkm_wr32(device, 0x100c18, dniso); + nvkm_mask(device, 0x100c14, 0x00000000, 0x00000001); + nvkm_wr32(device, 0x100c1c, hostnb); + nvkm_mask(device, 0x100c14, 0x00000000, 0x00000002); + nvkm_wr32(device, 0x100c24, flush); + nvkm_mask(device, 0x100c14, 0x00000000, 0x00010000); + return 0; +} + +static const struct nvkm_ram_func +mcp77_ram_func = { + .init = mcp77_ram_init, +}; + +int +mcp77_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 rsvd_head = ( 256 * 1024); /* vga memory */ + u32 rsvd_tail = (1024 * 1024) + 0x1000; /* vbios etc + poller mem */ + u64 base = (u64)nvkm_rd32(device, 0x100e10) << 12; + u64 size = (u64)nvkm_rd32(device, 0x100e14) << 12; + struct mcp77_ram *ram; + int ret; + + if (!(ram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + *pram = &ram->base; + + ret = nvkm_ram_ctor(&mcp77_ram_func, fb, NVKM_RAM_TYPE_STOLEN, + size, &ram->base); + if (ret) + return ret; + + ram->poller_base = size - rsvd_tail; + ram->base.stolen = base; + nvkm_mm_fini(&ram->base.vram); + + return nvkm_mm_init(&ram->base.vram, NVKM_RAM_MM_NORMAL, + rsvd_head >> NVKM_RAM_MM_SHIFT, + (size - rsvd_head - rsvd_tail) >> + NVKM_RAM_MM_SHIFT, 1); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c new file mode 100644 index 000000000..cc764a93f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c @@ -0,0 +1,65 @@ +/* + * 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 "ram.h" +#include "regsnv04.h" + +const struct nvkm_ram_func +nv04_ram_func = { +}; + +int +nv04_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 boot0 = nvkm_rd32(device, NV04_PFB_BOOT_0); + u64 size; + enum nvkm_ram_type type; + + if (boot0 & 0x00000100) { + size = ((boot0 >> 12) & 0xf) * 2 + 2; + size *= 1024 * 1024; + } else { + switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { + case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: + size = 32 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: + size = 16 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: + size = 8 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: + size = 4 * 1024 * 1024; + break; + } + } + + if ((boot0 & 0x00000038) <= 0x10) + type = NVKM_RAM_TYPE_SGRAM; + else + type = NVKM_RAM_TYPE_SDRAM; + + return nvkm_ram_new_(&nv04_ram_func, fb, type, size, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c new file mode 100644 index 000000000..afe54e323 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c @@ -0,0 +1,40 @@ +/* + * 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 "ram.h" + +int +nv10_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 size = nvkm_rd32(device, 0x10020c) & 0xff000000; + u32 cfg0 = nvkm_rd32(device, 0x100200); + enum nvkm_ram_type type; + + if (cfg0 & 0x00000001) + type = NVKM_RAM_TYPE_DDR1; + else + type = NVKM_RAM_TYPE_SDRAM; + + return nvkm_ram_new_(&nv04_ram_func, fb, type, size, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c new file mode 100644 index 000000000..18241c6ba --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c @@ -0,0 +1,56 @@ +/* + * 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 "ram.h" + +int +nv1a_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct pci_dev *bridge; + u32 mem, mib; + int domain = 0; + struct pci_dev *pdev = NULL; + + if (dev_is_pci(fb->subdev.device->dev)) + pdev = to_pci_dev(fb->subdev.device->dev); + + if (pdev) + domain = pci_domain_nr(pdev->bus); + + bridge = pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(0, 1)); + if (!bridge) { + nvkm_error(&fb->subdev, "no bridge device\n"); + return -ENODEV; + } + + if (fb->subdev.device->chipset == 0x1a) { + pci_read_config_dword(bridge, 0x7c, &mem); + mib = ((mem >> 6) & 31) + 1; + } else { + pci_read_config_dword(bridge, 0x84, &mem); + mib = ((mem >> 4) & 127) + 1; + } + + return nvkm_ram_new_(&nv04_ram_func, fb, NVKM_RAM_TYPE_STOLEN, + mib * 1024 * 1024, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c new file mode 100644 index 000000000..71d63d7da --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c @@ -0,0 +1,48 @@ +/* + * 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 "ram.h" + +int +nv20_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 pbus1218 = nvkm_rd32(device, 0x001218); + u32 size = (nvkm_rd32(device, 0x10020c) & 0xff000000); + enum nvkm_ram_type type = NVKM_RAM_TYPE_UNKNOWN; + int ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: type = NVKM_RAM_TYPE_SDRAM; break; + case 0x00000100: type = NVKM_RAM_TYPE_DDR1 ; break; + case 0x00000200: type = NVKM_RAM_TYPE_GDDR3; break; + case 0x00000300: type = NVKM_RAM_TYPE_GDDR2; break; + } + + ret = nvkm_ram_new_(&nv04_ram_func, fb, type, size, pram); + if (ret) + return ret; + + (*pram)->parts = (nvkm_rd32(device, 0x100200) & 0x00000003) + 1; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c new file mode 100644 index 000000000..97b3a28ca --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c @@ -0,0 +1,223 @@ +/* + * 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 "ramnv40.h" + +#include +#include +#include +#include +#include +#include + +static int +nv40_ram_calc(struct nvkm_ram *base, u32 freq) +{ + struct nv40_ram *ram = nv40_ram(base); + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_pll pll; + int N1, M1, N2, M2; + int log2P, ret; + + ret = nvbios_pll_parse(bios, 0x04, &pll); + if (ret) { + nvkm_error(subdev, "mclk pll data not found\n"); + return ret; + } + + ret = nv04_pll_calc(subdev, &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; +} + +static int +nv40_ram_prog(struct nvkm_ram *base) +{ + struct nv40_ram *ram = nv40_ram(base); + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + 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 = nvkm_rd32(device, 0x600808 + (i * 0x2000)); + u32 cnt = 0; + do { + if (vbl != nvkm_rd32(device, 0x600808 + (i * 0x2000))) { + nvkm_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01); + sr1[i] = nvkm_rd08(device, 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; + + nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x600808 + (i * 0x2000)); + if (!(tmp & 0x00010000)) + break; + ); + + nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x600808 + (i * 0x2000)); + if ( (tmp & 0x00010000)) + break; + ); + + nvkm_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01); + nvkm_wr08(device, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20); + } + + /* prepare ram for reclocking */ + nvkm_wr32(device, 0x1002d4, 0x00000001); /* precharge */ + nvkm_wr32(device, 0x1002d0, 0x00000001); /* refresh */ + nvkm_wr32(device, 0x1002d0, 0x00000001); /* refresh */ + nvkm_mask(device, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */ + nvkm_wr32(device, 0x1002dc, 0x00000001); /* enable self-refresh */ + + /* change the PLL of each memory partition */ + nvkm_mask(device, 0x00c040, 0x0000c000, 0x00000000); + switch (device->chipset) { + case 0x40: + case 0x45: + case 0x41: + case 0x42: + case 0x47: + nvkm_mask(device, 0x004044, 0xc0771100, ram->ctrl); + nvkm_mask(device, 0x00402c, 0xc0771100, ram->ctrl); + nvkm_wr32(device, 0x004048, ram->coef); + nvkm_wr32(device, 0x004030, ram->coef); + fallthrough; + case 0x43: + case 0x49: + case 0x4b: + nvkm_mask(device, 0x004038, 0xc0771100, ram->ctrl); + nvkm_wr32(device, 0x00403c, ram->coef); + fallthrough; + default: + nvkm_mask(device, 0x004020, 0xc0771100, ram->ctrl); + nvkm_wr32(device, 0x004024, ram->coef); + break; + } + udelay(100); + nvkm_mask(device, 0x00c040, 0x0000c000, 0x0000c000); + + /* re-enable normal operation of memory controller */ + nvkm_wr32(device, 0x1002dc, 0x00000000); + nvkm_mask(device, 0x100210, 0x80000000, 0x80000000); + udelay(100); + + /* execute memory reset script from vbios */ + if (!bit_entry(bios, 'M', &M)) + nvbios_init(subdev, nvbios_rd16(bios, M.offset + 0x00)); + + /* 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; + + nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x600808 + (i * 0x2000)); + if ( (tmp & 0x00010000)) + break; + ); + + nvkm_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01); + nvkm_wr08(device, 0x0c03c5 + (i * 0x2000), sr1[i]); + } + + return 0; +} + +static void +nv40_ram_tidy(struct nvkm_ram *base) +{ +} + +static const struct nvkm_ram_func +nv40_ram_func = { + .calc = nv40_ram_calc, + .prog = nv40_ram_prog, + .tidy = nv40_ram_tidy, +}; + +int +nv40_ram_new_(struct nvkm_fb *fb, enum nvkm_ram_type type, u64 size, + struct nvkm_ram **pram) +{ + struct nv40_ram *ram; + if (!(ram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + *pram = &ram->base; + return nvkm_ram_ctor(&nv40_ram_func, fb, type, size, &ram->base); +} + +int +nv40_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 pbus1218 = nvkm_rd32(device, 0x001218); + u32 size = nvkm_rd32(device, 0x10020c) & 0xff000000; + enum nvkm_ram_type type = NVKM_RAM_TYPE_UNKNOWN; + int ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: type = NVKM_RAM_TYPE_SDRAM; break; + case 0x00000100: type = NVKM_RAM_TYPE_DDR1 ; break; + case 0x00000200: type = NVKM_RAM_TYPE_GDDR3; break; + case 0x00000300: type = NVKM_RAM_TYPE_DDR2 ; break; + } + + ret = nv40_ram_new_(fb, type, size, pram); + if (ret) + return ret; + + (*pram)->parts = (nvkm_rd32(device, 0x100200) & 0x00000003) + 1; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.h new file mode 100644 index 000000000..a87de0871 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV40_FB_RAM_H__ +#define __NV40_FB_RAM_H__ +#define nv40_ram(p) container_of((p), struct nv40_ram, base) +#include "ram.h" + +struct nv40_ram { + struct nvkm_ram base; + u32 ctrl; + u32 coef; +}; + +int nv40_ram_new_(struct nvkm_fb *fb, enum nvkm_ram_type, u64, + struct nvkm_ram **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c new file mode 100644 index 000000000..d3fea3726 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c @@ -0,0 +1,48 @@ +/* + * 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 "ramnv40.h" + +int +nv41_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 size = nvkm_rd32(device, 0x10020c) & 0xff000000; + u32 fb474 = nvkm_rd32(device, 0x100474); + enum nvkm_ram_type type = NVKM_RAM_TYPE_UNKNOWN; + int ret; + + if (fb474 & 0x00000004) + type = NVKM_RAM_TYPE_GDDR3; + if (fb474 & 0x00000002) + type = NVKM_RAM_TYPE_DDR2; + if (fb474 & 0x00000001) + type = NVKM_RAM_TYPE_DDR1; + + ret = nv40_ram_new_(fb, type, size, pram); + if (ret) + return ret; + + (*pram)->parts = (nvkm_rd32(device, 0x100200) & 0x00000003) + 1; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c new file mode 100644 index 000000000..ab2630e5e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c @@ -0,0 +1,42 @@ +/* + * 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 "ramnv40.h" + +int +nv44_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 size = nvkm_rd32(device, 0x10020c) & 0xff000000; + u32 fb474 = nvkm_rd32(device, 0x100474); + enum nvkm_ram_type type = NVKM_RAM_TYPE_UNKNOWN; + + if (fb474 & 0x00000004) + type = NVKM_RAM_TYPE_GDDR3; + if (fb474 & 0x00000002) + type = NVKM_RAM_TYPE_DDR2; + if (fb474 & 0x00000001) + type = NVKM_RAM_TYPE_DDR1; + + return nv40_ram_new_(fb, type, size, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c new file mode 100644 index 000000000..946ca7c2e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c @@ -0,0 +1,48 @@ +/* + * 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 "ramnv40.h" + +int +nv49_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 size = nvkm_rd32(device, 0x10020c) & 0xff000000; + u32 fb914 = nvkm_rd32(device, 0x100914); + enum nvkm_ram_type type = NVKM_RAM_TYPE_UNKNOWN; + int ret; + + switch (fb914 & 0x00000003) { + case 0x00000000: type = NVKM_RAM_TYPE_DDR1 ; break; + case 0x00000001: type = NVKM_RAM_TYPE_DDR2 ; break; + case 0x00000002: type = NVKM_RAM_TYPE_GDDR3; break; + case 0x00000003: break; + } + + ret = nv40_ram_new_(fb, type, size, pram); + if (ret) + return ret; + + (*pram)->parts = (nvkm_rd32(device, 0x100200) & 0x00000003) + 1; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c new file mode 100644 index 000000000..02b8bdbc8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c @@ -0,0 +1,33 @@ +/* + * 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 "ram.h" + +int +nv4e_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nvkm_device *device = fb->subdev.device; + u32 size = nvkm_rd32(device, 0x10020c) & 0xff000000; + return nvkm_ram_new_(&nv04_ram_func, fb, NVKM_RAM_TYPE_UNKNOWN, + size, pram); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c new file mode 100644 index 000000000..7b1eb44ff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c @@ -0,0 +1,641 @@ +/* + * 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 + */ +#define nv50_ram(p) container_of((p), struct nv50_ram, base) +#include "ram.h" +#include "ramseq.h" +#include "nv50.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nv50_ramseq { + struct hwsq base; + struct hwsq_reg r_0x002504; + struct hwsq_reg r_0x004008; + struct hwsq_reg r_0x00400c; + struct hwsq_reg r_0x00c040; + struct hwsq_reg r_0x100200; + struct hwsq_reg r_0x100210; + struct hwsq_reg r_0x10021c; + struct hwsq_reg r_0x1002d0; + struct hwsq_reg r_0x1002d4; + struct hwsq_reg r_0x1002dc; + struct hwsq_reg r_0x10053c; + struct hwsq_reg r_0x1005a0; + struct hwsq_reg r_0x1005a4; + struct hwsq_reg r_0x100710; + struct hwsq_reg r_0x100714; + struct hwsq_reg r_0x100718; + struct hwsq_reg r_0x10071c; + struct hwsq_reg r_0x100da0; + 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 hwsq_reg r_gpio[4]; +}; + +struct nv50_ram { + struct nvkm_ram base; + struct nv50_ramseq hwsq; +}; + +#define T(t) cfg->timing_10_##t +static int +nv50_ram_timing_calc(struct nv50_ram *ram, u32 *timing) +{ + struct nvbios_ramcfg *cfg = &ram->base.target.bios; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + u32 cur2, cur4, cur7, cur8; + u8 unkt3b; + + cur2 = nvkm_rd32(device, 0x100228); + cur4 = nvkm_rd32(device, 0x100230); + cur7 = nvkm_rd32(device, 0x10023c); + cur8 = nvkm_rd32(device, 0x100240); + + switch ((!T(CWL)) * ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + T(CWL) = T(CL) - 1; + break; + case NVKM_RAM_TYPE_GDDR3: + T(CWL) = ((cur2 & 0xff000000) >> 24) + 1; + break; + } + + /* XXX: N=1 is not proper statistics */ + if (device->chipset == 0xa0) { + unkt3b = 0x19 + ram->base.next->bios.rammap_00_16_40; + timing[6] = (0x2d + T(CL) - T(CWL) + + ram->base.next->bios.rammap_00_16_40) << 16 | + T(CWL) << 8 | + (0x2f + T(CL) - T(CWL)); + } else { + unkt3b = 0x16; + timing[6] = (0x2b + T(CL) - T(CWL)) << 16 | + max_t(s8, T(CWL) - 2, 1) << 8 | + (0x2e + T(CL) - T(CWL)); + } + + timing[0] = (T(RP) << 24 | T(RAS) << 16 | T(RFC) << 8 | T(RC)); + timing[1] = (T(WR) + 1 + T(CWL)) << 24 | + max_t(u8, T(18), 1) << 16 | + (T(WTR) + 1 + T(CWL)) << 8 | + (3 + T(CL) - T(CWL)); + timing[2] = (T(CWL) - 1) << 24 | + (T(RRD) << 16) | + (T(RCDWR) << 8) | + T(RCDRD); + timing[3] = (unkt3b - 2 + T(CL)) << 24 | + unkt3b << 16 | + (T(CL) - 1) << 8 | + (T(CL) - 1); + timing[4] = (cur4 & 0xffff0000) | + T(13) << 8 | + T(13); + timing[5] = T(RFC) << 24 | + max_t(u8, T(RCDRD), T(RCDWR)) << 16 | + T(RP); + /* Timing 6 is already done above */ + timing[7] = (cur7 & 0xff00ffff) | (T(CL) - 1) << 16; + timing[8] = (cur8 & 0xffffff00); + + /* XXX: P.version == 1 only has DDR2 and GDDR3? */ + if (ram->base.type == NVKM_RAM_TYPE_DDR2) { + timing[5] |= (T(CL) + 3) << 8; + timing[8] |= (T(CL) - 4); + } else + if (ram->base.type == NVKM_RAM_TYPE_GDDR3) { + timing[5] |= (T(CL) + 2) << 8; + timing[8] |= (T(CL) - 2); + } + + nvkm_debug(subdev, " 220: %08x %08x %08x %08x\n", + timing[0], timing[1], timing[2], timing[3]); + nvkm_debug(subdev, " 230: %08x %08x %08x %08x\n", + timing[4], timing[5], timing[6], timing[7]); + nvkm_debug(subdev, " 240: %08x\n", timing[8]); + return 0; +} + +static int +nv50_ram_timing_read(struct nv50_ram *ram, u32 *timing) +{ + unsigned int i; + struct nvbios_ramcfg *cfg = &ram->base.target.bios; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + + for (i = 0; i <= 8; i++) + timing[i] = nvkm_rd32(device, 0x100220 + (i * 4)); + + /* Derive the bare minimum for the MR calculation to succeed */ + cfg->timing_ver = 0x10; + T(CL) = (timing[3] & 0xff) + 1; + + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + T(CWL) = T(CL) - 1; + break; + case NVKM_RAM_TYPE_GDDR3: + T(CWL) = ((timing[2] & 0xff000000) >> 24) + 1; + break; + default: + return -ENOSYS; + } + + T(WR) = ((timing[1] >> 24) & 0xff) - 1 - T(CWL); + + return 0; +} +#undef T + +static void +nvkm_sddr2_dll_reset(struct nv50_ramseq *hwsq) +{ + ram_mask(hwsq, mr[0], 0x100, 0x100); + ram_mask(hwsq, mr[0], 0x100, 0x000); + ram_nsec(hwsq, 24000); +} + +static void +nv50_ram_gpio(struct nv50_ramseq *hwsq, u8 tag, u32 val) +{ + struct nvkm_gpio *gpio = hwsq->base.subdev->device->gpio; + struct dcb_gpio_func func; + u32 reg, sh, gpio_val; + int ret; + + if (nvkm_gpio_get(gpio, 0, tag, DCB_GPIO_UNUSED) != val) { + ret = nvkm_gpio_find(gpio, 0, tag, DCB_GPIO_UNUSED, &func); + if (ret) + return; + + reg = func.line >> 3; + sh = (func.line & 0x7) << 2; + gpio_val = ram_rd32(hwsq, gpio[reg]); + + if (gpio_val & (8 << sh)) + val = !val; + if (!(func.log[1] & 1)) + val = !val; + + ram_mask(hwsq, gpio[reg], (0x3 << sh), ((val | 0x2) << sh)); + ram_nsec(hwsq, 20000); + } +} + +static int +nv50_ram_calc(struct nvkm_ram *base, u32 freq) +{ + struct nv50_ram *ram = nv50_ram(base); + struct nv50_ramseq *hwsq = &ram->hwsq; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_perfE perfE; + struct nvbios_pll mpll; + struct nvkm_ram_data *next; + u8 ver, hdr, cnt, len, strap, size; + u32 data; + u32 r100da0, r004008, unk710, unk714, unk718, unk71c; + int N1, M1, N2, M2, P; + int ret, i; + u32 timing[9]; + + next = &ram->base.target; + next->freq = freq; + ram->base.next = next; + + /* lookup closest matching performance table entry for frequency */ + i = 0; + do { + data = nvbios_perfEp(bios, i++, &ver, &hdr, &cnt, + &size, &perfE); + if (!data || (ver < 0x25 || ver >= 0x40) || + (size < 2)) { + nvkm_error(subdev, "invalid/missing perftab entry\n"); + return -EINVAL; + } + } while (perfE.memory < freq); + + nvbios_rammapEp_from_perf(bios, data, hdr, &next->bios); + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(subdev); + if (strap >= cnt) { + nvkm_error(subdev, "invalid ramcfg strap\n"); + return -EINVAL; + } + + data = nvbios_rammapSp_from_perf(bios, data + hdr, size, strap, + &next->bios); + if (!data) { + nvkm_error(subdev, "invalid/missing rammap entry "); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + if (next->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, next->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, &next->bios); + if (!data || ver != 0x10 || hdr < 0x12) { + nvkm_error(subdev, "invalid/missing timing entry " + "%02x %04x %02x %02x\n", + strap, data, ver, hdr); + return -EINVAL; + } + nv50_ram_timing_calc(ram, timing); + } else { + nv50_ram_timing_read(ram, timing); + } + + ret = ram_init(hwsq, subdev); + if (ret) + return ret; + + /* Determine ram-specific MR values */ + ram->base.mr[0] = ram_rd32(hwsq, mr[0]); + ram->base.mr[1] = ram_rd32(hwsq, mr[1]); + ram->base.mr[2] = ram_rd32(hwsq, mr[2]); + + switch (ram->base.type) { + case NVKM_RAM_TYPE_GDDR3: + ret = nvkm_gddr3_calc(&ram->base); + break; + default: + ret = -ENOSYS; + break; + } + + if (ret) { + nvkm_error(subdev, "Could not calculate MR\n"); + return ret; + } + + if (subdev->device->chipset <= 0x96 && !next->bios.ramcfg_00_03_02) + ram_mask(hwsq, 0x100710, 0x00000200, 0x00000000); + + /* Always disable this bit during reclock */ + ram_mask(hwsq, 0x100200, 0x00000800, 0x00000000); + + ram_wait_vblank(hwsq); + 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_nsec(hwsq, 2000); + + if (next->bios.timing_10_ODT) + nv50_ram_gpio(hwsq, 0x2e, 1); + + 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(subdev, &mpll, freq, + &N1, &M1, &N2, &M2, &P); + if (ret <= 0) + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + /* XXX: 750MHz seems rather arbitrary */ + if (freq <= 750000) { + r100da0 = 0x00000010; + r004008 = 0x90000000; + } else { + r100da0 = 0x00000000; + r004008 = 0x80000000; + } + + r004008 |= (mpll.bias_p << 19) | (P << 22) | (P << 16); + + ram_mask(hwsq, 0x00c040, 0xc000c000, 0x0000c000); + /* XXX: Is rammap_00_16_40 the DLL bit we've seen in GT215? Why does + * it have a different rammap bit from DLLoff? */ + ram_mask(hwsq, 0x004008, 0x00004200, 0x00000200 | + next->bios.rammap_00_16_40 << 14); + ram_mask(hwsq, 0x00400c, 0x0000ffff, (N1 << 8) | M1); + ram_mask(hwsq, 0x004008, 0x91ff0000, r004008); + + /* XXX: GDDR3 only? */ + if (subdev->device->chipset >= 0x92) + ram_wr32(hwsq, 0x100da0, r100da0); + + nv50_ram_gpio(hwsq, 0x18, !next->bios.ramcfg_FBVDDQ); + ram_nsec(hwsq, 64000); /*XXX*/ + ram_nsec(hwsq, 32000); /*XXX*/ + + ram_mask(hwsq, 0x004008, 0x00002200, 0x00002000); + + ram_wr32(hwsq, 0x1002dc, 0x00000000); /* disable self-refresh */ + ram_wr32(hwsq, 0x1002d4, 0x00000001); /* disable self-refresh */ + ram_wr32(hwsq, 0x100210, 0x80000000); /* enable auto-refresh */ + + ram_nsec(hwsq, 12000); + + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + ram_nuke(hwsq, mr[0]); /* force update */ + ram_mask(hwsq, mr[0], 0x000, 0x000); + break; + case NVKM_RAM_TYPE_GDDR3: + ram_nuke(hwsq, mr[1]); /* force update */ + ram_wr32(hwsq, mr[1], ram->base.mr[1]); + ram_nuke(hwsq, mr[0]); /* force update */ + ram_wr32(hwsq, mr[0], ram->base.mr[0]); + break; + default: + break; + } + + ram_mask(hwsq, timing[3], 0xffffffff, timing[3]); + ram_mask(hwsq, timing[1], 0xffffffff, timing[1]); + ram_mask(hwsq, timing[6], 0xffffffff, timing[6]); + ram_mask(hwsq, timing[7], 0xffffffff, timing[7]); + ram_mask(hwsq, timing[8], 0xffffffff, timing[8]); + ram_mask(hwsq, timing[0], 0xffffffff, timing[0]); + ram_mask(hwsq, timing[2], 0xffffffff, timing[2]); + ram_mask(hwsq, timing[4], 0xffffffff, timing[4]); + ram_mask(hwsq, timing[5], 0xffffffff, timing[5]); + + if (!next->bios.ramcfg_00_03_02) + ram_mask(hwsq, 0x10021c, 0x00010000, 0x00000000); + ram_mask(hwsq, 0x100200, 0x00001000, !next->bios.ramcfg_00_04_02 << 12); + + /* XXX: A lot of this could be "chipset"/"ram type" specific stuff */ + unk710 = ram_rd32(hwsq, 0x100710) & ~0x00000100; + unk714 = ram_rd32(hwsq, 0x100714) & ~0xf0000020; + unk718 = ram_rd32(hwsq, 0x100718) & ~0x00000100; + unk71c = ram_rd32(hwsq, 0x10071c) & ~0x00000100; + if (subdev->device->chipset <= 0x96) { + unk710 &= ~0x0000006e; + unk714 &= ~0x00000100; + + if (!next->bios.ramcfg_00_03_08) + unk710 |= 0x00000060; + if (!next->bios.ramcfg_FBVDDQ) + unk714 |= 0x00000100; + if ( next->bios.ramcfg_00_04_04) + unk710 |= 0x0000000e; + } else { + unk710 &= ~0x00000001; + + if (!next->bios.ramcfg_00_03_08) + unk710 |= 0x00000001; + } + + if ( next->bios.ramcfg_00_03_01) + unk71c |= 0x00000100; + if ( next->bios.ramcfg_00_03_02) + unk710 |= 0x00000100; + if (!next->bios.ramcfg_00_03_08) + unk714 |= 0x00000020; + if ( next->bios.ramcfg_00_04_04) + unk714 |= 0x70000000; + if ( next->bios.ramcfg_00_04_20) + unk718 |= 0x00000100; + + ram_mask(hwsq, 0x100714, 0xffffffff, unk714); + ram_mask(hwsq, 0x10071c, 0xffffffff, unk71c); + ram_mask(hwsq, 0x100718, 0xffffffff, unk718); + ram_mask(hwsq, 0x100710, 0xffffffff, unk710); + + /* XXX: G94 does not even test these regs in trace. Harmless we do it, + * but why is it omitted? */ + if (next->bios.rammap_00_16_20) { + ram_wr32(hwsq, 0x1005a0, next->bios.ramcfg_00_07 << 16 | + next->bios.ramcfg_00_06 << 8 | + next->bios.ramcfg_00_05); + ram_wr32(hwsq, 0x1005a4, next->bios.ramcfg_00_09 << 8 | + next->bios.ramcfg_00_08); + ram_mask(hwsq, 0x10053c, 0x00001000, 0x00000000); + } else { + ram_mask(hwsq, 0x10053c, 0x00001000, 0x00001000); + } + ram_mask(hwsq, mr[1], 0xffffffff, ram->base.mr[1]); + + if (!next->bios.timing_10_ODT) + nv50_ram_gpio(hwsq, 0x2e, 0); + + /* Reset DLL */ + if (!next->bios.ramcfg_DLLoff) + nvkm_sddr2_dll_reset(hwsq); + + 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 */ + + if (next->bios.rammap_00_17_02) + ram_mask(hwsq, 0x100200, 0x00000800, 0x00000800); + if (!next->bios.rammap_00_16_40) + ram_mask(hwsq, 0x004008, 0x00004000, 0x00000000); + if (next->bios.ramcfg_00_03_02) + ram_mask(hwsq, 0x10021c, 0x00010000, 0x00010000); + if (subdev->device->chipset <= 0x96 && next->bios.ramcfg_00_03_02) + ram_mask(hwsq, 0x100710, 0x00000200, 0x00000200); + + return 0; +} + +static int +nv50_ram_prog(struct nvkm_ram *base) +{ + struct nv50_ram *ram = nv50_ram(base); + struct nvkm_device *device = ram->base.fb->subdev.device; + ram_exec(&ram->hwsq, nvkm_boolopt(device->cfgopt, "NvMemExec", true)); + return 0; +} + +static void +nv50_ram_tidy(struct nvkm_ram *base) +{ + struct nv50_ram *ram = nv50_ram(base); + ram_exec(&ram->hwsq, false); +} + +static const struct nvkm_ram_func +nv50_ram_func = { + .calc = nv50_ram_calc, + .prog = nv50_ram_prog, + .tidy = nv50_ram_tidy, +}; + +static u32 +nv50_fb_vram_rblock(struct nvkm_ram *ram) +{ + struct nvkm_subdev *subdev = &ram->fb->subdev; + struct nvkm_device *device = subdev->device; + int colbits, rowbitsa, rowbitsb, banks; + u64 rowsize, predicted; + u32 r0, r4, rt, rblock_size; + + r0 = nvkm_rd32(device, 0x100200); + r4 = nvkm_rd32(device, 0x100204); + rt = nvkm_rd32(device, 0x100250); + nvkm_debug(subdev, "memcfg %08x %08x %08x %08x\n", + r0, r4, rt, nvkm_rd32(device, 0x001540)); + + colbits = (r4 & 0x0000f000) >> 12; + rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; + rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; + banks = 1 << (((r4 & 0x03000000) >> 24) + 2); + + rowsize = ram->parts * banks * (1 << colbits) * 8; + predicted = rowsize << rowbitsa; + if (r0 & 0x00000004) + predicted += rowsize << rowbitsb; + + if (predicted != ram->size) { + nvkm_warn(subdev, "memory controller reports %d MiB VRAM\n", + (u32)(ram->size >> 20)); + } + + rblock_size = rowsize; + if (rt & 1) + rblock_size *= 3; + + nvkm_debug(subdev, "rblock %d bytes\n", rblock_size); + return rblock_size; +} + +int +nv50_ram_ctor(const struct nvkm_ram_func *func, + struct nvkm_fb *fb, struct nvkm_ram *ram) +{ + struct nvkm_device *device = fb->subdev.device; + struct nvkm_bios *bios = device->bios; + const u32 rsvd_head = ( 256 * 1024); /* vga memory */ + const u32 rsvd_tail = (1024 * 1024); /* vbios etc */ + u64 size = nvkm_rd32(device, 0x10020c); + enum nvkm_ram_type type = NVKM_RAM_TYPE_UNKNOWN; + int ret; + + switch (nvkm_rd32(device, 0x100714) & 0x00000007) { + case 0: type = NVKM_RAM_TYPE_DDR1; break; + case 1: + if (nvkm_fb_bios_memtype(bios) == NVKM_RAM_TYPE_DDR3) + type = NVKM_RAM_TYPE_DDR3; + else + type = NVKM_RAM_TYPE_DDR2; + break; + case 2: type = NVKM_RAM_TYPE_GDDR3; break; + case 3: type = NVKM_RAM_TYPE_GDDR4; break; + case 4: type = NVKM_RAM_TYPE_GDDR5; break; + default: + break; + } + + size = (size & 0x000000ff) << 32 | (size & 0xffffff00); + + ret = nvkm_ram_ctor(func, fb, type, size, ram); + if (ret) + return ret; + + ram->part_mask = (nvkm_rd32(device, 0x001540) & 0x00ff0000) >> 16; + ram->parts = hweight8(ram->part_mask); + ram->ranks = (nvkm_rd32(device, 0x100200) & 0x4) ? 2 : 1; + nvkm_mm_fini(&ram->vram); + + return nvkm_mm_init(&ram->vram, NVKM_RAM_MM_NORMAL, + rsvd_head >> NVKM_RAM_MM_SHIFT, + (size - rsvd_head - rsvd_tail) >> NVKM_RAM_MM_SHIFT, + nv50_fb_vram_rblock(ram) >> NVKM_RAM_MM_SHIFT); +} + +int +nv50_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct nv50_ram *ram; + int ret, i; + + if (!(ram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + *pram = &ram->base; + + ret = nv50_ram_ctor(&nv50_ram_func, fb, &ram->base); + if (ret) + return ret; + + 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_0x100200 = hwsq_reg(0x100200); + ram->hwsq.r_0x100210 = hwsq_reg(0x100210); + ram->hwsq.r_0x10021c = hwsq_reg(0x10021c); + ram->hwsq.r_0x1002d0 = hwsq_reg(0x1002d0); + ram->hwsq.r_0x1002d4 = hwsq_reg(0x1002d4); + ram->hwsq.r_0x1002dc = hwsq_reg(0x1002dc); + ram->hwsq.r_0x10053c = hwsq_reg(0x10053c); + ram->hwsq.r_0x1005a0 = hwsq_reg(0x1005a0); + ram->hwsq.r_0x1005a4 = hwsq_reg(0x1005a4); + ram->hwsq.r_0x100710 = hwsq_reg(0x100710); + ram->hwsq.r_0x100714 = hwsq_reg(0x100714); + ram->hwsq.r_0x100718 = hwsq_reg(0x100718); + ram->hwsq.r_0x10071c = hwsq_reg(0x10071c); + ram->hwsq.r_0x100da0 = hwsq_stride(0x100da0, 4, ram->base.part_mask); + 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); + } + + ram->hwsq.r_gpio[0] = hwsq_reg(0x00e104); + ram->hwsq.r_gpio[1] = hwsq_reg(0x00e108); + ram->hwsq.r_gpio[2] = hwsq_reg(0x00e120); + ram->hwsq.r_gpio[3] = hwsq_reg(0x00e124); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h new file mode 100644 index 000000000..aba5b7378 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FBRAM_SEQ_H__ +#define __NVKM_FBRAM_SEQ_H__ +#include + +#define ram_init(s,p) hwsq_init(&(s)->base, (p)) +#define ram_exec(s,e) hwsq_exec(&(s)->base, (e)) +#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_rd32(s,r) hwsq_rd32(&(s)->base, &(s)->r_##r) +#define ram_wr32(s,r,d) hwsq_wr32(&(s)->base, &(s)->r_##r, (d)) +#define ram_nuke(s,r) hwsq_nuke(&(s)->base, &(s)->r_##r) +#define ram_mask(s,r,m,d) hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define ram_setf(s,f,d) hwsq_setf(&(s)->base, (f), (d)) +#define ram_wait(s,f,d) hwsq_wait(&(s)->base, (f), (d)) +#define ram_wait_vblank(s) hwsq_wait_vblank(&(s)->base) +#define ram_nsec(s,n) hwsq_nsec(&(s)->base, (n)) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h new file mode 100644 index 000000000..8098cd77d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FB_REGS_04_H__ +#define __NVKM_FB_REGS_04_H__ + +#define NV04_PFB_BOOT_0 0x00100000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 +# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 +# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 +# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 +# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 +#define NV04_PFB_CFG0 0x00100200 + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c new file mode 100644 index 000000000..4dcd8742f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c @@ -0,0 +1,100 @@ +/* + * Copyright 2014 Roy Spliet + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Roy Spliet + * Ben Skeggs + */ +#include "priv.h" +#include "ram.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramddr2_cl[] = { + { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 7 }, + { -1 } +}; + +static const struct ramxlat +ramddr2_wr[] = { + { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 6 }, + { -1 } +}; + +int +nvkm_sddr2_calc(struct nvkm_ram *ram) +{ + int CL, WR, DLL = 0, ODT = 0; + + switch (ram->next->bios.timing_ver) { + case 0x10: + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_DLLoff; + ODT = ram->next->bios.timing_10_ODT & 3; + break; + case 0x20: + CL = (ram->next->bios.timing[1] & 0x0000001f); + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + break; + default: + return -ENOSYS; + } + + if (ram->next->bios.timing_ver == 0x20 || + ram->next->bios.ramcfg_timing == 0xff) { + ODT = (ram->mr[1] & 0x004) >> 2 | + (ram->mr[1] & 0x040) >> 5; + } + + CL = ramxlat(ramddr2_cl, CL); + WR = ramxlat(ramddr2_wr, WR); + if (CL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf70; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x07) << 4; + + ram->mr[1] &= ~0x045; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= !DLL; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c new file mode 100644 index 000000000..eca8a445e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c @@ -0,0 +1,120 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Roy Spliet + */ +#include "priv.h" +#include "ram.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 }, { 15, 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 }, { 10, 5 }, + { -1 } +}; + +int +nvkm_sddr3_calc(struct nvkm_ram *ram) +{ + int CWL, CL, WR, DLL = 0, ODT = 0; + + DLL = !ram->next->bios.ramcfg_DLLoff; + + switch (ram->next->bios.timing_ver) { + case 0x10: + if (ram->next->bios.timing_hdr < 0x17) { + /* XXX: NV50: Get CWL from the timing register */ + return -ENOSYS; + } + CWL = ram->next->bios.timing_10_CWL; + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + ODT = ram->next->bios.timing_10_ODT; + break; + case 0x20: + CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0; + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + /* XXX: Get these values from the VBIOS instead */ + ODT = (ram->mr[1] & 0x004) >> 2 | + (ram->mr[1] & 0x040) >> 5 | + (ram->mr[1] & 0x200) >> 7; + break; + default: + return -ENOSYS; + } + + CWL = ramxlat(ramddr3_cwl, CWL); + CL = ramxlat(ramddr3_cl, CL); + WR = ramxlat(ramddr3_wr, WR); + if (CL < 0 || CWL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf74; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x0e) << 3; + ram->mr[0] |= (CL & 0x01) << 2; + + ram->mr[1] &= ~0x245; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= (ODT & 0x4) << 7; + ram->mr[1] |= !DLL; + + ram->mr[2] &= ~0x038; + ram->mr[2] |= (CWL & 0x07) << 3; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild new file mode 100644 index 000000000..8e7cd9d27 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/fuse/base.o +nvkm-y += nvkm/subdev/fuse/nv50.o +nvkm-y += nvkm/subdev/fuse/gf100.o +nvkm-y += nvkm/subdev/fuse/gm107.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c new file mode 100644 index 000000000..375dfce09 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c @@ -0,0 +1,54 @@ +/* + * Copyright 2014 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +u32 +nvkm_fuse_read(struct nvkm_fuse *fuse, u32 addr) +{ + return fuse->func->read(fuse, addr); +} + +static void * +nvkm_fuse_dtor(struct nvkm_subdev *subdev) +{ + return nvkm_fuse(subdev); +} + +static const struct nvkm_subdev_func +nvkm_fuse = { + .dtor = nvkm_fuse_dtor, +}; + +int +nvkm_fuse_new_(const struct nvkm_fuse_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fuse **pfuse) +{ + struct nvkm_fuse *fuse; + if (!(fuse = *pfuse = kzalloc(sizeof(*fuse), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_fuse, device, type, inst, &fuse->subdev); + fuse->func = func; + spin_lock_init(&fuse->lock); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c new file mode 100644 index 000000000..01f770654 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c @@ -0,0 +1,54 @@ +/* + * Copyright 2014 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +static u32 +gf100_fuse_read(struct nvkm_fuse *fuse, u32 addr) +{ + struct nvkm_device *device = fuse->subdev.device; + unsigned long flags; + u32 fuse_enable, unk, val; + + /* racy if another part of nvkm start writing to these regs */ + spin_lock_irqsave(&fuse->lock, flags); + fuse_enable = nvkm_mask(device, 0x022400, 0x800, 0x800); + unk = nvkm_mask(device, 0x021000, 0x1, 0x1); + val = nvkm_rd32(device, 0x021100 + addr); + nvkm_wr32(device, 0x021000, unk); + nvkm_wr32(device, 0x022400, fuse_enable); + spin_unlock_irqrestore(&fuse->lock, flags); + return val; +} + +static const struct nvkm_fuse_func +gf100_fuse = { + .read = gf100_fuse_read, +}; + +int +gf100_fuse_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fuse **pfuse) +{ + return nvkm_fuse_new_(&gf100_fuse, device, type, inst, pfuse); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c new file mode 100644 index 000000000..7dc99492f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +static u32 +gm107_fuse_read(struct nvkm_fuse *fuse, u32 addr) +{ + struct nvkm_device *device = fuse->subdev.device; + return nvkm_rd32(device, 0x021100 + addr); +} + +static const struct nvkm_fuse_func +gm107_fuse = { + .read = gm107_fuse_read, +}; + +int +gm107_fuse_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fuse **pfuse) +{ + return nvkm_fuse_new_(&gm107_fuse, device, type, inst, pfuse); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c new file mode 100644 index 000000000..2505e8e1c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c @@ -0,0 +1,52 @@ +/* + * Copyright 2014 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +static u32 +nv50_fuse_read(struct nvkm_fuse *fuse, u32 addr) +{ + struct nvkm_device *device = fuse->subdev.device; + unsigned long flags; + u32 fuse_enable, val; + + /* racy if another part of nvkm start writing to this reg */ + spin_lock_irqsave(&fuse->lock, flags); + fuse_enable = nvkm_mask(device, 0x001084, 0x800, 0x800); + val = nvkm_rd32(device, 0x021000 + addr); + nvkm_wr32(device, 0x001084, fuse_enable); + spin_unlock_irqrestore(&fuse->lock, flags); + return val; +} + +static const struct nvkm_fuse_func +nv50_fuse = { + .read = &nv50_fuse_read, +}; + +int +nv50_fuse_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_fuse **pfuse) +{ + return nvkm_fuse_new_(&nv50_fuse, device, type, inst, pfuse); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h new file mode 100644 index 000000000..e83d0c30d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_FUSE_PRIV_H__ +#define __NVKM_FUSE_PRIV_H__ +#define nvkm_fuse(p) container_of((p), struct nvkm_fuse, subdev) +#include + +struct nvkm_fuse_func { + u32 (*read)(struct nvkm_fuse *, u32 addr); +}; + +int nvkm_fuse_new_(const struct nvkm_fuse_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_fuse **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild new file mode 100644 index 000000000..efbbaa080 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/gpio/base.o +nvkm-y += nvkm/subdev/gpio/nv10.o +nvkm-y += nvkm/subdev/gpio/nv50.o +nvkm-y += nvkm/subdev/gpio/g94.o +nvkm-y += nvkm/subdev/gpio/gf119.o +nvkm-y += nvkm/subdev/gpio/gk104.o +nvkm-y += nvkm/subdev/gpio/ga102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c new file mode 100644 index 000000000..048bcc70c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c @@ -0,0 +1,256 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +static int +nvkm_gpio_drive(struct nvkm_gpio *gpio, int idx, int line, int dir, int out) +{ + return gpio->func->drive(gpio, line, dir, out); +} + +static int +nvkm_gpio_sense(struct nvkm_gpio *gpio, int idx, int line) +{ + return gpio->func->sense(gpio, line); +} + +void +nvkm_gpio_reset(struct nvkm_gpio *gpio, u8 func) +{ + if (gpio->func->reset) + gpio->func->reset(gpio, func); +} + +int +nvkm_gpio_find(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line, + struct dcb_gpio_func *func) +{ + struct nvkm_device *device = gpio->subdev.device; + struct nvkm_bios *bios = device->bios; + u8 ver, len; + u16 data; + + if (line == 0xff && tag == 0xff) + return -EINVAL; + + data = dcb_gpio_match(bios, idx, tag, line, &ver, &len, func); + if (data) + return 0; + + /* Apple iMac G4 NV18 */ + if (device->quirk && device->quirk->tv_gpio) { + if (tag == DCB_GPIO_TVDAC0) { + *func = (struct dcb_gpio_func) { + .func = DCB_GPIO_TVDAC0, + .line = device->quirk->tv_gpio, + .log[0] = 0, + .log[1] = 1, + }; + return 0; + } + } + + return -ENOENT; +} + +int +nvkm_gpio_set(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line, int state) +{ + struct dcb_gpio_func func; + int ret; + + ret = nvkm_gpio_find(gpio, idx, tag, line, &func); + if (ret == 0) { + int dir = !!(func.log[state] & 0x02); + int out = !!(func.log[state] & 0x01); + ret = nvkm_gpio_drive(gpio, idx, func.line, dir, out); + } + + return ret; +} + +int +nvkm_gpio_get(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line) +{ + struct dcb_gpio_func func; + int ret; + + ret = nvkm_gpio_find(gpio, idx, tag, line, &func); + if (ret == 0) { + ret = nvkm_gpio_sense(gpio, idx, func.line); + if (ret >= 0) + ret = (ret == (func.log[1] & 1)); + } + + return ret; +} + +static void +nvkm_gpio_intr_fini(struct nvkm_event *event, int type, int index) +{ + struct nvkm_gpio *gpio = container_of(event, typeof(*gpio), event); + gpio->func->intr_mask(gpio, type, 1 << index, 0); +} + +static void +nvkm_gpio_intr_init(struct nvkm_event *event, int type, int index) +{ + struct nvkm_gpio *gpio = container_of(event, typeof(*gpio), event); + gpio->func->intr_mask(gpio, type, 1 << index, 1 << index); +} + +static int +nvkm_gpio_intr_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nvkm_gpio_ntfy_req *req = data; + if (!WARN_ON(size != sizeof(*req))) { + notify->size = sizeof(struct nvkm_gpio_ntfy_rep); + notify->types = req->mask; + notify->index = req->line; + return 0; + } + return -EINVAL; +} + +static const struct nvkm_event_func +nvkm_gpio_intr_func = { + .ctor = nvkm_gpio_intr_ctor, + .init = nvkm_gpio_intr_init, + .fini = nvkm_gpio_intr_fini, +}; + +static void +nvkm_gpio_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_gpio *gpio = nvkm_gpio(subdev); + u32 hi, lo, i; + + gpio->func->intr_stat(gpio, &hi, &lo); + + for (i = 0; (hi | lo) && i < gpio->func->lines; i++) { + struct nvkm_gpio_ntfy_rep rep = { + .mask = (NVKM_GPIO_HI * !!(hi & (1 << i))) | + (NVKM_GPIO_LO * !!(lo & (1 << i))), + }; + nvkm_event_send(&gpio->event, rep.mask, i, &rep, sizeof(rep)); + } +} + +static int +nvkm_gpio_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_gpio *gpio = nvkm_gpio(subdev); + u32 mask = (1ULL << gpio->func->lines) - 1; + + gpio->func->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0); + gpio->func->intr_stat(gpio, &mask, &mask); + return 0; +} + +static const struct dmi_system_id gpio_reset_ids[] = { + { + .ident = "Apple Macbook 10,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"), + } + }, + { } +}; + +static enum dcb_gpio_func_name power_checks[] = { + DCB_GPIO_THERM_EXT_POWER_EVENT, + DCB_GPIO_POWER_ALERT, + DCB_GPIO_EXT_POWER_LOW, +}; + +static int +nvkm_gpio_init(struct nvkm_subdev *subdev) +{ + struct nvkm_gpio *gpio = nvkm_gpio(subdev); + struct dcb_gpio_func func; + int ret; + int i; + + if (dmi_check_system(gpio_reset_ids)) + nvkm_gpio_reset(gpio, DCB_GPIO_UNUSED); + + if (nvkm_boolopt(subdev->device->cfgopt, "NvPowerChecks", true)) { + for (i = 0; i < ARRAY_SIZE(power_checks); ++i) { + ret = nvkm_gpio_find(gpio, 0, power_checks[i], + DCB_GPIO_UNUSED, &func); + if (ret) + continue; + + ret = nvkm_gpio_get(gpio, 0, func.func, func.line); + if (!ret) + continue; + + nvkm_error(&gpio->subdev, + "GPU is missing power, check its power " + "cables. Boot with " + "nouveau.config=NvPowerChecks=0 to " + "disable.\n"); + return -EINVAL; + } + } + + return 0; +} + +static void * +nvkm_gpio_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_gpio *gpio = nvkm_gpio(subdev); + nvkm_event_fini(&gpio->event); + return gpio; +} + +static const struct nvkm_subdev_func +nvkm_gpio = { + .dtor = nvkm_gpio_dtor, + .init = nvkm_gpio_init, + .fini = nvkm_gpio_fini, + .intr = nvkm_gpio_intr, +}; + +int +nvkm_gpio_new_(const struct nvkm_gpio_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_gpio **pgpio) +{ + struct nvkm_gpio *gpio; + + if (!(gpio = *pgpio = kzalloc(sizeof(*gpio), GFP_KERNEL))) + return -ENOMEM; + + nvkm_subdev_ctor(&nvkm_gpio, device, type, inst, &gpio->subdev); + gpio->func = func; + + return nvkm_event_init(&nvkm_gpio_intr_func, 2, func->lines, + &gpio->event); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c new file mode 100644 index 000000000..114728ccd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c @@ -0,0 +1,75 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +void +g94_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 intr0 = nvkm_rd32(device, 0x00e054); + u32 intr1 = nvkm_rd32(device, 0x00e074); + u32 stat0 = nvkm_rd32(device, 0x00e050) & intr0; + u32 stat1 = nvkm_rd32(device, 0x00e070) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nvkm_wr32(device, 0x00e054, intr0); + nvkm_wr32(device, 0x00e074, intr1); +} + +void +g94_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 inte0 = nvkm_rd32(device, 0x00e050); + u32 inte1 = nvkm_rd32(device, 0x00e070); + if (type & NVKM_GPIO_LO) + inte0 = (inte0 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff); + mask >>= 16; + data >>= 16; + if (type & NVKM_GPIO_LO) + inte1 = (inte1 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte1 = (inte1 & ~mask) | data; + nvkm_wr32(device, 0x00e050, inte0); + nvkm_wr32(device, 0x00e070, inte1); +} + +static const struct nvkm_gpio_func +g94_gpio = { + .lines = 32, + .intr_stat = g94_gpio_intr_stat, + .intr_mask = g94_gpio_intr_mask, + .drive = nv50_gpio_drive, + .sense = nv50_gpio_sense, + .reset = nv50_gpio_reset, +}; + +int +g94_gpio_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gpio **pgpio) +{ + return nvkm_gpio_new_(&g94_gpio, device, type, inst, pgpio); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c new file mode 100644 index 000000000..4a96f926b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c @@ -0,0 +1,119 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +static void +ga102_gpio_reset(struct nvkm_gpio *gpio, u8 match) +{ + struct nvkm_device *device = gpio->subdev.device; + struct nvkm_bios *bios = device->bios; + u8 ver, len; + u16 entry; + int ent = -1; + + while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) { + u32 data = nvbios_rd32(bios, entry); + u8 line = (data & 0x0000003f); + u8 defs = !!(data & 0x00000080); + u8 func = (data & 0x0000ff00) >> 8; + u8 unk0 = (data & 0x00ff0000) >> 16; + u8 unk1 = (data & 0x1f000000) >> 24; + + if ( func == DCB_GPIO_UNUSED || + (match != DCB_GPIO_UNUSED && match != func)) + continue; + + nvkm_gpio_set(gpio, 0, func, line, defs); + + nvkm_mask(device, 0x021200 + (line * 4), 0xff, unk0); + if (unk1--) + nvkm_mask(device, 0x00d740 + (unk1 * 4), 0xff, line); + } +} + +static int +ga102_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 data = ((dir ^ 1) << 13) | (out << 12); + nvkm_mask(device, 0x021200 + (line * 4), 0x00003000, data); + nvkm_mask(device, 0x00d604, 0x00000001, 0x00000001); /* update? */ + return 0; +} + +static int +ga102_gpio_sense(struct nvkm_gpio *gpio, int line) +{ + struct nvkm_device *device = gpio->subdev.device; + return !!(nvkm_rd32(device, 0x021200 + (line * 4)) & 0x00004000); +} + +static void +ga102_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 intr0 = nvkm_rd32(device, 0x021640); + u32 intr1 = nvkm_rd32(device, 0x02164c); + u32 stat0 = nvkm_rd32(device, 0x021648) & intr0; + u32 stat1 = nvkm_rd32(device, 0x021654) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nvkm_wr32(device, 0x021640, intr0); + nvkm_wr32(device, 0x02164c, intr1); +} + +static void +ga102_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 inte0 = nvkm_rd32(device, 0x021648); + u32 inte1 = nvkm_rd32(device, 0x021654); + if (type & NVKM_GPIO_LO) + inte0 = (inte0 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff); + mask >>= 16; + data >>= 16; + if (type & NVKM_GPIO_LO) + inte1 = (inte1 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte1 = (inte1 & ~mask) | data; + nvkm_wr32(device, 0x021648, inte0); + nvkm_wr32(device, 0x021654, inte1); +} + +static const struct nvkm_gpio_func +ga102_gpio = { + .lines = 32, + .intr_stat = ga102_gpio_intr_stat, + .intr_mask = ga102_gpio_intr_mask, + .drive = ga102_gpio_drive, + .sense = ga102_gpio_sense, + .reset = ga102_gpio_reset, +}; + +int +ga102_gpio_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gpio **pgpio) +{ + return nvkm_gpio_new_(&ga102_gpio, device, type, inst, pgpio); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf119.c new file mode 100644 index 000000000..ecb19e4f5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf119.c @@ -0,0 +1,87 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +void +gf119_gpio_reset(struct nvkm_gpio *gpio, u8 match) +{ + struct nvkm_device *device = gpio->subdev.device; + struct nvkm_bios *bios = device->bios; + u8 ver, len; + u16 entry; + int ent = -1; + + while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) { + u32 data = nvbios_rd32(bios, entry); + u8 line = (data & 0x0000003f); + u8 defs = !!(data & 0x00000080); + u8 func = (data & 0x0000ff00) >> 8; + u8 unk0 = (data & 0x00ff0000) >> 16; + u8 unk1 = (data & 0x1f000000) >> 24; + + if ( func == DCB_GPIO_UNUSED || + (match != DCB_GPIO_UNUSED && match != func)) + continue; + + nvkm_gpio_set(gpio, 0, func, line, defs); + + nvkm_mask(device, 0x00d610 + (line * 4), 0xff, unk0); + if (unk1--) + nvkm_mask(device, 0x00d740 + (unk1 * 4), 0xff, line); + } +} + +int +gf119_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 data = ((dir ^ 1) << 13) | (out << 12); + nvkm_mask(device, 0x00d610 + (line * 4), 0x00003000, data); + nvkm_mask(device, 0x00d604, 0x00000001, 0x00000001); /* update? */ + return 0; +} + +int +gf119_gpio_sense(struct nvkm_gpio *gpio, int line) +{ + struct nvkm_device *device = gpio->subdev.device; + return !!(nvkm_rd32(device, 0x00d610 + (line * 4)) & 0x00004000); +} + +static const struct nvkm_gpio_func +gf119_gpio = { + .lines = 32, + .intr_stat = g94_gpio_intr_stat, + .intr_mask = g94_gpio_intr_mask, + .drive = gf119_gpio_drive, + .sense = gf119_gpio_sense, + .reset = gf119_gpio_reset, +}; + +int +gf119_gpio_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gpio **pgpio) +{ + return nvkm_gpio_new_(&gf119_gpio, device, type, inst, pgpio); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c new file mode 100644 index 000000000..c0e4cdb45 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c @@ -0,0 +1,75 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static void +gk104_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 intr0 = nvkm_rd32(device, 0x00dc00); + u32 intr1 = nvkm_rd32(device, 0x00dc80); + u32 stat0 = nvkm_rd32(device, 0x00dc08) & intr0; + u32 stat1 = nvkm_rd32(device, 0x00dc88) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nvkm_wr32(device, 0x00dc00, intr0); + nvkm_wr32(device, 0x00dc80, intr1); +} + +static void +gk104_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 inte0 = nvkm_rd32(device, 0x00dc08); + u32 inte1 = nvkm_rd32(device, 0x00dc88); + if (type & NVKM_GPIO_LO) + inte0 = (inte0 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff); + mask >>= 16; + data >>= 16; + if (type & NVKM_GPIO_LO) + inte1 = (inte1 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte1 = (inte1 & ~mask) | data; + nvkm_wr32(device, 0x00dc08, inte0); + nvkm_wr32(device, 0x00dc88, inte1); +} + +static const struct nvkm_gpio_func +gk104_gpio = { + .lines = 32, + .intr_stat = gk104_gpio_intr_stat, + .intr_mask = gk104_gpio_intr_mask, + .drive = gf119_gpio_drive, + .sense = gf119_gpio_sense, + .reset = gf119_gpio_reset, +}; + +int +gk104_gpio_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gpio **pgpio) +{ + return nvkm_gpio_new_(&gk104_gpio, device, type, inst, pgpio); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c new file mode 100644 index 000000000..48ad29b56 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2009 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "priv.h" + +static int +nv10_gpio_sense(struct nvkm_gpio *gpio, int line) +{ + struct nvkm_device *device = gpio->subdev.device; + if (line < 2) { + line = line * 16; + line = nvkm_rd32(device, 0x600818) >> line; + return !!(line & 0x0100); + } else + if (line < 10) { + line = (line - 2) * 4; + line = nvkm_rd32(device, 0x60081c) >> line; + return !!(line & 0x04); + } else + if (line < 14) { + line = (line - 10) * 4; + line = nvkm_rd32(device, 0x600850) >> line; + return !!(line & 0x04); + } + + return -EINVAL; +} + +static int +nv10_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 reg, mask, data; + + if (line < 2) { + line = line * 16; + reg = 0x600818; + mask = 0x00000011; + data = (dir << 4) | out; + } else + if (line < 10) { + line = (line - 2) * 4; + reg = 0x60081c; + mask = 0x00000003; + data = (dir << 1) | out; + } else + if (line < 14) { + line = (line - 10) * 4; + reg = 0x600850; + mask = 0x00000003; + data = (dir << 1) | out; + } else { + return -EINVAL; + } + + nvkm_mask(device, reg, mask << line, data << line); + return 0; +} + +static void +nv10_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 intr = nvkm_rd32(device, 0x001104); + u32 stat = nvkm_rd32(device, 0x001144) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nvkm_wr32(device, 0x001104, intr); +} + +static void +nv10_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 inte = nvkm_rd32(device, 0x001144); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nvkm_wr32(device, 0x001144, inte); +} + +static const struct nvkm_gpio_func +nv10_gpio = { + .lines = 16, + .intr_stat = nv10_gpio_intr_stat, + .intr_mask = nv10_gpio_intr_mask, + .drive = nv10_gpio_drive, + .sense = nv10_gpio_sense, +}; + +int +nv10_gpio_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gpio **pgpio) +{ + return nvkm_gpio_new_(&nv10_gpio, device, type, inst, pgpio); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c new file mode 100644 index 000000000..b86c49762 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c @@ -0,0 +1,133 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +void +nv50_gpio_reset(struct nvkm_gpio *gpio, u8 match) +{ + struct nvkm_device *device = gpio->subdev.device; + struct nvkm_bios *bios = device->bios; + u8 ver, len; + u16 entry; + int ent = -1; + + while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) { + static const u32 regs[] = { 0xe100, 0xe28c }; + u32 data = nvbios_rd32(bios, entry); + u8 line = (data & 0x0000001f); + u8 func = (data & 0x0000ff00) >> 8; + u8 defs = !!(data & 0x01000000); + u8 unk0 = !!(data & 0x02000000); + u8 unk1 = !!(data & 0x04000000); + u32 val = (unk1 << 16) | unk0; + u32 reg = regs[line >> 4]; + u32 lsh = line & 0x0f; + + if ( func == DCB_GPIO_UNUSED || + (match != DCB_GPIO_UNUSED && match != func)) + continue; + + nvkm_gpio_set(gpio, 0, func, line, defs); + + nvkm_mask(device, reg, 0x00010001 << lsh, val << lsh); + } +} + +static int +nv50_gpio_location(int line, u32 *reg, u32 *shift) +{ + const u32 nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; + + if (line >= 32) + return -EINVAL; + + *reg = nv50_gpio_reg[line >> 3]; + *shift = (line & 7) << 2; + return 0; +} + +int +nv50_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 reg, shift; + + if (nv50_gpio_location(line, ®, &shift)) + return -EINVAL; + + nvkm_mask(device, reg, 3 << shift, (((dir ^ 1) << 1) | out) << shift); + return 0; +} + +int +nv50_gpio_sense(struct nvkm_gpio *gpio, int line) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 reg, shift; + + if (nv50_gpio_location(line, ®, &shift)) + return -EINVAL; + + return !!(nvkm_rd32(device, reg) & (4 << shift)); +} + +static void +nv50_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 intr = nvkm_rd32(device, 0x00e054); + u32 stat = nvkm_rd32(device, 0x00e050) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nvkm_wr32(device, 0x00e054, intr); +} + +static void +nv50_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data) +{ + struct nvkm_device *device = gpio->subdev.device; + u32 inte = nvkm_rd32(device, 0x00e050); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nvkm_wr32(device, 0x00e050, inte); +} + +static const struct nvkm_gpio_func +nv50_gpio = { + .lines = 16, + .intr_stat = nv50_gpio_intr_stat, + .intr_mask = nv50_gpio_intr_mask, + .drive = nv50_gpio_drive, + .sense = nv50_gpio_sense, + .reset = nv50_gpio_reset, +}; + +int +nv50_gpio_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gpio **pgpio) +{ + return nvkm_gpio_new_(&nv50_gpio, device, type, inst, pgpio); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h new file mode 100644 index 000000000..6590d8116 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_GPIO_PRIV_H__ +#define __NVKM_GPIO_PRIV_H__ +#define nvkm_gpio(p) container_of((p), struct nvkm_gpio, subdev) +#include + +struct nvkm_gpio_func { + int lines; + + /* read and ack pending interrupts, returning only data + * for lines that have not been masked off, while still + * performing the ack for anything that was pending. + */ + void (*intr_stat)(struct nvkm_gpio *, u32 *, u32 *); + + /* mask on/off interrupts for hi/lo transitions on a + * given set of gpio lines + */ + void (*intr_mask)(struct nvkm_gpio *, u32, u32, u32); + + /* configure gpio direction and output value */ + int (*drive)(struct nvkm_gpio *, int line, int dir, int out); + + /* sense current state of given gpio line */ + int (*sense)(struct nvkm_gpio *, int line); + + /*XXX*/ + void (*reset)(struct nvkm_gpio *, u8); +}; + +int nvkm_gpio_new_(const struct nvkm_gpio_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_gpio **); + +void nv50_gpio_reset(struct nvkm_gpio *, u8); +int nv50_gpio_drive(struct nvkm_gpio *, int, int, int); +int nv50_gpio_sense(struct nvkm_gpio *, int); + +void g94_gpio_intr_stat(struct nvkm_gpio *, u32 *, u32 *); +void g94_gpio_intr_mask(struct nvkm_gpio *, u32, u32, u32); + +void gf119_gpio_reset(struct nvkm_gpio *, u8); +int gf119_gpio_drive(struct nvkm_gpio *, int, int, int); +int gf119_gpio_sense(struct nvkm_gpio *, int); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/Kbuild new file mode 100644 index 000000000..67cc3b320 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/Kbuild @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/gsp/base.o +nvkm-y += nvkm/subdev/gsp/gv100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c new file mode 100644 index 000000000..22574886b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c @@ -0,0 +1,57 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include +#include +#include +#include + +static void * +nvkm_gsp_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_gsp *gsp = nvkm_gsp(subdev); + nvkm_falcon_dtor(&gsp->falcon); + return gsp; +} + +static const struct nvkm_subdev_func +nvkm_gsp = { + .dtor = nvkm_gsp_dtor, +}; + +int +nvkm_gsp_new_(const struct nvkm_gsp_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_gsp **pgsp) +{ + struct nvkm_gsp *gsp; + + if (!(gsp = *pgsp = kzalloc(sizeof(*gsp), GFP_KERNEL))) + return -ENOMEM; + + nvkm_subdev_ctor(&nvkm_gsp, device, type, inst, &gsp->subdev); + + fwif = nvkm_firmware_load(&gsp->subdev, fwif, "Gsp", gsp); + if (IS_ERR(fwif)) + return PTR_ERR(fwif); + + return nvkm_falcon_ctor(fwif->flcn, &gsp->subdev, gsp->subdev.name, 0, &gsp->falcon); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gv100.c new file mode 100644 index 000000000..6c4ef62a7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gv100.c @@ -0,0 +1,56 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +static const struct nvkm_falcon_func +gv100_gsp_flcn = { + .fbif = 0x600, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .bind_context = gp102_sec2_flcn_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = gp102_sec2_flcn_enable, + .disable = nvkm_falcon_v1_disable, +}; + +static int +gv100_gsp_nofw(struct nvkm_gsp *gsp, int ver, const struct nvkm_gsp_fwif *fwif) +{ + return 0; +} + +static struct nvkm_gsp_fwif +gv100_gsp[] = { + { -1, gv100_gsp_nofw, &gv100_gsp_flcn }, + {} +}; + +int +gv100_gsp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_gsp **pgsp) +{ + return nvkm_gsp_new_(gv100_gsp, device, type, inst, pgsp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/priv.h new file mode 100644 index 000000000..19381ddd3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/priv.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_GSP_PRIV_H__ +#define __NVKM_GSP_PRIV_H__ +#include +enum nvkm_acr_lsf_id; + +struct nvkm_gsp_fwif { + int version; + int (*load)(struct nvkm_gsp *, int ver, const struct nvkm_gsp_fwif *); + const struct nvkm_falcon_func *flcn; +}; + +int nvkm_gsp_new_(const struct nvkm_gsp_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_gsp **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild new file mode 100644 index 000000000..819703913 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/i2c/base.o +nvkm-y += nvkm/subdev/i2c/nv04.o +nvkm-y += nvkm/subdev/i2c/nv4e.o +nvkm-y += nvkm/subdev/i2c/nv50.o +nvkm-y += nvkm/subdev/i2c/g94.o +nvkm-y += nvkm/subdev/i2c/gf117.o +nvkm-y += nvkm/subdev/i2c/gf119.o +nvkm-y += nvkm/subdev/i2c/gk104.o +nvkm-y += nvkm/subdev/i2c/gk110.o +nvkm-y += nvkm/subdev/i2c/gm200.o + +nvkm-y += nvkm/subdev/i2c/pad.o +nvkm-y += nvkm/subdev/i2c/padnv04.o +nvkm-y += nvkm/subdev/i2c/padnv4e.o +nvkm-y += nvkm/subdev/i2c/padnv50.o +nvkm-y += nvkm/subdev/i2c/padg94.o +nvkm-y += nvkm/subdev/i2c/padgf119.o +nvkm-y += nvkm/subdev/i2c/padgm200.o + +nvkm-y += nvkm/subdev/i2c/bus.o +nvkm-y += nvkm/subdev/i2c/busnv04.o +nvkm-y += nvkm/subdev/i2c/busnv4e.o +nvkm-y += nvkm/subdev/i2c/busnv50.o +nvkm-y += nvkm/subdev/i2c/busgf119.o +nvkm-y += nvkm/subdev/i2c/bit.o + +nvkm-y += nvkm/subdev/i2c/aux.o +nvkm-y += nvkm/subdev/i2c/auxg94.o +nvkm-y += nvkm/subdev/i2c/auxgf119.o +nvkm-y += nvkm/subdev/i2c/auxgm200.o + +nvkm-y += nvkm/subdev/i2c/anx9805.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c new file mode 100644 index 000000000..dd391809f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c @@ -0,0 +1,278 @@ +/* + * 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 + */ +#define anx9805_pad(p) container_of((p), struct anx9805_pad, base) +#define anx9805_bus(p) container_of((p), struct anx9805_bus, base) +#define anx9805_aux(p) container_of((p), struct anx9805_aux, base) +#include "aux.h" +#include "bus.h" + +struct anx9805_pad { + struct nvkm_i2c_pad base; + struct nvkm_i2c_bus *bus; + u8 addr; +}; + +struct anx9805_bus { + struct nvkm_i2c_bus base; + struct anx9805_pad *pad; + u8 addr; +}; + +static int +anx9805_bus_xfer(struct nvkm_i2c_bus *base, struct i2c_msg *msgs, int num) +{ + struct anx9805_bus *bus = anx9805_bus(base); + struct anx9805_pad *pad = bus->pad; + struct i2c_adapter *adap = &pad->bus->i2c; + struct i2c_msg *msg = msgs; + int ret = -ETIMEDOUT; + int i, j, cnt = num; + u8 seg = 0x00, off = 0x00, tmp; + + tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x10; + nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x10); + nvkm_wri2cr(adap, pad->addr, 0x07, tmp); + nvkm_wri2cr(adap, bus->addr, 0x43, 0x05); + mdelay(5); + + while (cnt--) { + if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) { + nvkm_wri2cr(adap, bus->addr, 0x40, msg->addr << 1); + nvkm_wri2cr(adap, bus->addr, 0x41, seg); + nvkm_wri2cr(adap, bus->addr, 0x42, off); + nvkm_wri2cr(adap, bus->addr, 0x44, msg->len); + nvkm_wri2cr(adap, bus->addr, 0x45, 0x00); + nvkm_wri2cr(adap, bus->addr, 0x43, 0x01); + for (i = 0; i < msg->len; i++) { + j = 0; + while (nvkm_rdi2cr(adap, bus->addr, 0x46) & 0x10) { + mdelay(5); + if (j++ == 32) + goto done; + } + msg->buf[i] = nvkm_rdi2cr(adap, bus->addr, 0x47); + } + } else + if (!(msg->flags & I2C_M_RD)) { + if (msg->addr == 0x50 && msg->len == 0x01) { + off = msg->buf[0]; + } else + if (msg->addr == 0x30 && msg->len == 0x01) { + seg = msg->buf[0]; + } else + goto done; + } else { + goto done; + } + msg++; + } + + ret = num; +done: + nvkm_wri2cr(adap, bus->addr, 0x43, 0x00); + return ret; +} + +static const struct nvkm_i2c_bus_func +anx9805_bus_func = { + .xfer = anx9805_bus_xfer, +}; + +static int +anx9805_bus_new(struct nvkm_i2c_pad *base, int id, u8 drive, + struct nvkm_i2c_bus **pbus) +{ + struct anx9805_pad *pad = anx9805_pad(base); + struct anx9805_bus *bus; + int ret; + + if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) + return -ENOMEM; + *pbus = &bus->base; + bus->pad = pad; + + ret = nvkm_i2c_bus_ctor(&anx9805_bus_func, &pad->base, id, &bus->base); + if (ret) + return ret; + + switch (pad->addr) { + case 0x39: bus->addr = 0x3d; break; + case 0x3b: bus->addr = 0x3f; break; + default: + return -ENOSYS; + } + + return 0; +} + +struct anx9805_aux { + struct nvkm_i2c_aux base; + struct anx9805_pad *pad; + u8 addr; +}; + +static int +anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry, + u8 type, u32 addr, u8 *data, u8 *size) +{ + struct anx9805_aux *aux = anx9805_aux(base); + struct anx9805_pad *pad = aux->pad; + struct i2c_adapter *adap = &pad->bus->i2c; + int i, ret = -ETIMEDOUT; + u8 buf[16] = {}; + u8 tmp; + + AUX_DBG(&aux->base, "%02x %05x %d", type, addr, *size); + + tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x04; + nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x04); + nvkm_wri2cr(adap, pad->addr, 0x07, tmp); + nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01); + + nvkm_wri2cr(adap, aux->addr, 0xe4, 0x80); + if (!(type & 1)) { + memcpy(buf, data, *size); + AUX_DBG(&aux->base, "%16ph", buf); + for (i = 0; i < *size; i++) + nvkm_wri2cr(adap, aux->addr, 0xf0 + i, buf[i]); + } + nvkm_wri2cr(adap, aux->addr, 0xe5, ((*size - 1) << 4) | type); + nvkm_wri2cr(adap, aux->addr, 0xe6, (addr & 0x000ff) >> 0); + nvkm_wri2cr(adap, aux->addr, 0xe7, (addr & 0x0ff00) >> 8); + nvkm_wri2cr(adap, aux->addr, 0xe8, (addr & 0xf0000) >> 16); + nvkm_wri2cr(adap, aux->addr, 0xe9, 0x01); + + i = 0; + while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xe9)) & 0x01) { + mdelay(5); + if (i++ == 32) + goto done; + } + + if ((tmp = nvkm_rdi2cr(adap, pad->addr, 0xf7)) & 0x01) { + ret = -EIO; + goto done; + } + + if (type & 1) { + for (i = 0; i < *size; i++) + buf[i] = nvkm_rdi2cr(adap, aux->addr, 0xf0 + i); + AUX_DBG(&aux->base, "%16ph", buf); + memcpy(data, buf, *size); + } + + ret = 0; +done: + nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01); + return ret; +} + +static int +anx9805_aux_lnk_ctl(struct nvkm_i2c_aux *base, + int link_nr, int link_bw, bool enh) +{ + struct anx9805_aux *aux = anx9805_aux(base); + struct anx9805_pad *pad = aux->pad; + struct i2c_adapter *adap = &pad->bus->i2c; + u8 tmp, i; + + AUX_DBG(&aux->base, "ANX9805 train %d %02x %d", + link_nr, link_bw, enh); + + nvkm_wri2cr(adap, aux->addr, 0xa0, link_bw); + nvkm_wri2cr(adap, aux->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); + nvkm_wri2cr(adap, aux->addr, 0xa2, 0x01); + nvkm_wri2cr(adap, aux->addr, 0xa8, 0x01); + + i = 0; + while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xa8)) & 0x01) { + mdelay(5); + if (i++ == 100) { + AUX_ERR(&aux->base, "link training timeout"); + return -ETIMEDOUT; + } + } + + if (tmp & 0x70) { + AUX_ERR(&aux->base, "link training failed"); + return -EIO; + } + + return 0; +} + +static const struct nvkm_i2c_aux_func +anx9805_aux_func = { + .xfer = anx9805_aux_xfer, + .lnk_ctl = anx9805_aux_lnk_ctl, +}; + +static int +anx9805_aux_new(struct nvkm_i2c_pad *base, int id, u8 drive, + struct nvkm_i2c_aux **pbus) +{ + struct anx9805_pad *pad = anx9805_pad(base); + struct anx9805_aux *aux; + int ret; + + if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL))) + return -ENOMEM; + *pbus = &aux->base; + aux->pad = pad; + + ret = nvkm_i2c_aux_ctor(&anx9805_aux_func, &pad->base, id, &aux->base); + if (ret) + return ret; + + switch (pad->addr) { + case 0x39: aux->addr = 0x38; break; + case 0x3b: aux->addr = 0x3c; break; + default: + return -ENOSYS; + } + + return 0; +} + +static const struct nvkm_i2c_pad_func +anx9805_pad_func = { + .bus_new_4 = anx9805_bus_new, + .aux_new_6 = anx9805_aux_new, +}; + +int +anx9805_pad_new(struct nvkm_i2c_bus *bus, int id, u8 addr, + struct nvkm_i2c_pad **ppad) +{ + struct anx9805_pad *pad; + + if (!(pad = kzalloc(sizeof(*pad), GFP_KERNEL))) + return -ENOMEM; + *ppad = &pad->base; + + nvkm_i2c_pad_ctor(&anx9805_pad_func, bus->pad->i2c, id, &pad->base); + pad->bus = bus; + pad->addr = addr; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c new file mode 100644 index 000000000..d063d0dc1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c @@ -0,0 +1,215 @@ +/* + * Copyright 2009 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 + +#include "aux.h" +#include "pad.h" + +static int +nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct nvkm_i2c_aux *aux = container_of(adap, typeof(*aux), i2c); + struct i2c_msg *msg = msgs; + int ret, mcnt = num; + + ret = nvkm_i2c_aux_acquire(aux); + if (ret) + return ret; + + while (mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; + + while (remaining) { + u8 cnt, retries, cmd; + + if (msg->flags & I2C_M_RD) + cmd = 1; + else + cmd = 0; + + if (mcnt || remaining > 16) + cmd |= 4; /* MOT */ + + for (retries = 0, cnt = 0; + retries < 32 && !cnt; + retries++) { + cnt = min_t(u8, remaining, 16); + ret = aux->func->xfer(aux, true, cmd, + msg->addr, ptr, &cnt); + if (ret < 0) + goto out; + } + if (!cnt) { + AUX_TRACE(aux, "no data after 32 retries"); + ret = -EIO; + goto out; + } + + ptr += cnt; + remaining -= cnt; + } + + msg++; + } + + ret = num; +out: + nvkm_i2c_aux_release(aux); + return ret; +} + +static u32 +nvkm_i2c_aux_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm +nvkm_i2c_aux_i2c_algo = { + .master_xfer = nvkm_i2c_aux_i2c_xfer, + .functionality = nvkm_i2c_aux_i2c_func +}; + +void +nvkm_i2c_aux_monitor(struct nvkm_i2c_aux *aux, bool monitor) +{ + struct nvkm_i2c_pad *pad = aux->pad; + AUX_TRACE(aux, "monitor: %s", str_yes_no(monitor)); + if (monitor) + nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_AUX); + else + nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_OFF); +} + +void +nvkm_i2c_aux_release(struct nvkm_i2c_aux *aux) +{ + struct nvkm_i2c_pad *pad = aux->pad; + AUX_TRACE(aux, "release"); + nvkm_i2c_pad_release(pad); + mutex_unlock(&aux->mutex); +} + +int +nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *aux) +{ + struct nvkm_i2c_pad *pad = aux->pad; + int ret; + + AUX_TRACE(aux, "acquire"); + mutex_lock(&aux->mutex); + + if (aux->enabled) + ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_AUX); + else + ret = -EIO; + + if (ret) + mutex_unlock(&aux->mutex); + return ret; +} + +int +nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type, + u32 addr, u8 *data, u8 *size) +{ + if (!*size && !aux->func->address_only) { + AUX_ERR(aux, "address-only transaction dropped"); + return -ENOSYS; + } + return aux->func->xfer(aux, retry, type, addr, data, size); +} + +int +nvkm_i2c_aux_lnk_ctl(struct nvkm_i2c_aux *aux, int nr, int bw, bool ef) +{ + if (aux->func->lnk_ctl) + return aux->func->lnk_ctl(aux, nr, bw, ef); + return -ENODEV; +} + +void +nvkm_i2c_aux_del(struct nvkm_i2c_aux **paux) +{ + struct nvkm_i2c_aux *aux = *paux; + if (aux && !WARN_ON(!aux->func)) { + AUX_TRACE(aux, "dtor"); + list_del(&aux->head); + i2c_del_adapter(&aux->i2c); + kfree(*paux); + *paux = NULL; + } +} + +void +nvkm_i2c_aux_init(struct nvkm_i2c_aux *aux) +{ + AUX_TRACE(aux, "init"); + mutex_lock(&aux->mutex); + aux->enabled = true; + mutex_unlock(&aux->mutex); +} + +void +nvkm_i2c_aux_fini(struct nvkm_i2c_aux *aux) +{ + AUX_TRACE(aux, "fini"); + mutex_lock(&aux->mutex); + aux->enabled = false; + mutex_unlock(&aux->mutex); +} + +int +nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *func, + struct nvkm_i2c_pad *pad, int id, + struct nvkm_i2c_aux *aux) +{ + struct nvkm_device *device = pad->i2c->subdev.device; + + aux->func = func; + aux->pad = pad; + aux->id = id; + mutex_init(&aux->mutex); + list_add_tail(&aux->head, &pad->i2c->aux); + AUX_TRACE(aux, "ctor"); + + snprintf(aux->i2c.name, sizeof(aux->i2c.name), "nvkm-%s-aux-%04x", + dev_name(device->dev), id); + aux->i2c.owner = THIS_MODULE; + aux->i2c.dev.parent = device->dev; + aux->i2c.algo = &nvkm_i2c_aux_i2c_algo; + return i2c_add_adapter(&aux->i2c); +} + +int +nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *func, + struct nvkm_i2c_pad *pad, int id, + struct nvkm_i2c_aux **paux) +{ + if (!(*paux = kzalloc(sizeof(**paux), GFP_KERNEL))) + return -ENOMEM; + return nvkm_i2c_aux_ctor(func, pad, id, *paux); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h new file mode 100644 index 000000000..f920eabf8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_I2C_AUX_H__ +#define __NVKM_I2C_AUX_H__ +#include "pad.h" + +static inline void +nvkm_i2c_aux_autodpcd(struct nvkm_i2c *i2c, int aux, bool enable) +{ + if (i2c->func->aux_autodpcd) + i2c->func->aux_autodpcd(i2c, aux, false); +} + +struct nvkm_i2c_aux_func { + bool address_only; + int (*xfer)(struct nvkm_i2c_aux *, bool retry, u8 type, + u32 addr, u8 *data, u8 *size); + int (*lnk_ctl)(struct nvkm_i2c_aux *, int link_nr, int link_bw, + bool enhanced_framing); +}; + +int nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, + int id, struct nvkm_i2c_aux *); +int nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, + int id, struct nvkm_i2c_aux **); +void nvkm_i2c_aux_del(struct nvkm_i2c_aux **); +void nvkm_i2c_aux_init(struct nvkm_i2c_aux *); +void nvkm_i2c_aux_fini(struct nvkm_i2c_aux *); +int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *, bool retry, u8 type, + u32 addr, u8 *data, u8 *size); + +int g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, + int, u8, struct nvkm_i2c_aux **); + +int g94_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); +int g94_i2c_aux_xfer(struct nvkm_i2c_aux *, bool, u8, u32, u8 *, u8 *); +int gf119_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); +int gm200_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); + +#define AUX_MSG(b,l,f,a...) do { \ + struct nvkm_i2c_aux *_aux = (b); \ + nvkm_##l(&_aux->pad->i2c->subdev, "aux %04x: "f"\n", _aux->id, ##a); \ +} while(0) +#define AUX_ERR(b,f,a...) AUX_MSG((b), error, f, ##a) +#define AUX_DBG(b,f,a...) AUX_MSG((b), debug, f, ##a) +#define AUX_TRACE(b,f,a...) AUX_MSG((b), trace, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c new file mode 100644 index 000000000..47068f6f9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c @@ -0,0 +1,194 @@ +/* + * Copyright 2015 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 busions 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 + */ +#define g94_i2c_aux(p) container_of((p), struct g94_i2c_aux, base) +#include "aux.h" + +struct g94_i2c_aux { + struct nvkm_i2c_aux base; + int ch; +}; + +static void +g94_i2c_aux_fini(struct g94_i2c_aux *aux) +{ + struct nvkm_device *device = aux->base.pad->i2c->subdev.device; + nvkm_mask(device, 0x00e4e4 + (aux->ch * 0x50), 0x00310000, 0x00000000); +} + +static int +g94_i2c_aux_init(struct g94_i2c_aux *aux) +{ + struct nvkm_device *device = aux->base.pad->i2c->subdev.device; + const u32 unksel = 1; /* nfi which to use, or if it matters.. */ + const u32 ureq = unksel ? 0x00100000 : 0x00200000; + const u32 urep = unksel ? 0x01000000 : 0x02000000; + u32 ctrl, timeout; + + /* wait up to 1ms for any previous transaction to be done... */ + timeout = 1000; + do { + ctrl = nvkm_rd32(device, 0x00e4e4 + (aux->ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR(&aux->base, "begin idle timeout %08x", ctrl); + return -EBUSY; + } + } while (ctrl & 0x03010000); + + /* set some magic, and wait up to 1ms for it to appear */ + nvkm_mask(device, 0x00e4e4 + (aux->ch * 0x50), 0x00300000, ureq); + timeout = 1000; + do { + ctrl = nvkm_rd32(device, 0x00e4e4 + (aux->ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR(&aux->base, "magic wait %08x", ctrl); + g94_i2c_aux_fini(aux); + return -EBUSY; + } + } while ((ctrl & 0x03000000) != urep); + + return 0; +} + +int +g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry, + u8 type, u32 addr, u8 *data, u8 *size) +{ + struct g94_i2c_aux *aux = g94_i2c_aux(obj); + struct nvkm_i2c *i2c = aux->base.pad->i2c; + struct nvkm_device *device = i2c->subdev.device; + const u32 base = aux->ch * 0x50; + u32 ctrl, stat, timeout, retries = 0; + u32 xbuf[4] = {}; + int ret, i; + + AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, *size); + + ret = g94_i2c_aux_init(aux); + if (ret < 0) + goto out; + + stat = nvkm_rd32(device, 0x00e4e8 + base); + if (!(stat & 0x10000000)) { + AUX_TRACE(&aux->base, "sink not detected"); + ret = -ENXIO; + goto out; + } + + nvkm_i2c_aux_autodpcd(i2c, aux->ch, false); + + if (!(type & 1)) { + memcpy(xbuf, data, *size); + for (i = 0; i < 16; i += 4) { + AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]); + nvkm_wr32(device, 0x00e4c0 + base + i, xbuf[i / 4]); + } + } + + ctrl = nvkm_rd32(device, 0x00e4e4 + base); + ctrl &= ~0x0001f1ff; + ctrl |= type << 12; + ctrl |= (*size ? (*size - 1) : 0x00000100); + nvkm_wr32(device, 0x00e4e0 + base, addr); + + /* (maybe) retry transaction a number of times on failure... */ + do { + /* reset, and delay a while if this is a retry */ + nvkm_wr32(device, 0x00e4e4 + base, 0x80000000 | ctrl); + nvkm_wr32(device, 0x00e4e4 + base, 0x00000000 | ctrl); + if (retries) + udelay(400); + + /* transaction request, wait up to 2ms for it to complete */ + nvkm_wr32(device, 0x00e4e4 + base, 0x00010000 | ctrl); + + timeout = 2000; + do { + ctrl = nvkm_rd32(device, 0x00e4e4 + base); + udelay(1); + if (!timeout--) { + AUX_ERR(&aux->base, "timeout %08x", ctrl); + ret = -EIO; + goto out_err; + } + } while (ctrl & 0x00010000); + ret = 0; + + /* read status, and check if transaction completed ok */ + stat = nvkm_mask(device, 0x00e4e8 + base, 0, 0); + if ((stat & 0x000f0000) == 0x00080000 || + (stat & 0x000f0000) == 0x00020000) + ret = 1; + if ((stat & 0x00000100)) + ret = -ETIMEDOUT; + if ((stat & 0x00000e00)) + ret = -EIO; + + AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat); + } while (ret && retry && retries++ < 32); + + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nvkm_rd32(device, 0x00e4d0 + base + i); + AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]); + } + memcpy(data, xbuf, *size); + *size = stat & 0x0000001f; + } +out_err: + nvkm_i2c_aux_autodpcd(i2c, aux->ch, true); +out: + g94_i2c_aux_fini(aux); + return ret < 0 ? ret : (stat & 0x000f0000) >> 16; +} + +int +g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *func, + struct nvkm_i2c_pad *pad, int index, u8 drive, + struct nvkm_i2c_aux **paux) +{ + struct g94_i2c_aux *aux; + + if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL))) + return -ENOMEM; + *paux = &aux->base; + + nvkm_i2c_aux_ctor(func, pad, index, &aux->base); + aux->ch = drive; + aux->base.intr = 1 << aux->ch; + return 0; +} + +static const struct nvkm_i2c_aux_func +g94_i2c_aux = { + .xfer = g94_i2c_aux_xfer, +}; + +int +g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, + struct nvkm_i2c_aux **paux) +{ + return g94_i2c_aux_new_(&g94_i2c_aux, pad, index, drive, paux); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c new file mode 100644 index 000000000..dab40cd8f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "aux.h" + +static const struct nvkm_i2c_aux_func +gf119_i2c_aux = { + .address_only = true, + .xfer = g94_i2c_aux_xfer, +}; + +int +gf119_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, + struct nvkm_i2c_aux **paux) +{ + return g94_i2c_aux_new_(&gf119_i2c_aux, pad, index, drive, paux); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c new file mode 100644 index 000000000..8bd1d442e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c @@ -0,0 +1,188 @@ +/* + * Copyright 2015 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 busions 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 + */ +#define gm200_i2c_aux(p) container_of((p), struct gm200_i2c_aux, base) +#include "aux.h" + +struct gm200_i2c_aux { + struct nvkm_i2c_aux base; + int ch; +}; + +static void +gm200_i2c_aux_fini(struct gm200_i2c_aux *aux) +{ + struct nvkm_device *device = aux->base.pad->i2c->subdev.device; + nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00710000, 0x00000000); +} + +static int +gm200_i2c_aux_init(struct gm200_i2c_aux *aux) +{ + struct nvkm_device *device = aux->base.pad->i2c->subdev.device; + const u32 unksel = 1; /* nfi which to use, or if it matters.. */ + const u32 ureq = unksel ? 0x00100000 : 0x00200000; + const u32 urep = unksel ? 0x01000000 : 0x02000000; + u32 ctrl, timeout; + + /* wait up to 1ms for any previous transaction to be done... */ + timeout = 1000; + do { + ctrl = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR(&aux->base, "begin idle timeout %08x", ctrl); + return -EBUSY; + } + } while (ctrl & 0x07010000); + + /* set some magic, and wait up to 1ms for it to appear */ + nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00700000, ureq); + timeout = 1000; + do { + ctrl = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR(&aux->base, "magic wait %08x", ctrl); + gm200_i2c_aux_fini(aux); + return -EBUSY; + } + } while ((ctrl & 0x07000000) != urep); + + return 0; +} + +static int +gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry, + u8 type, u32 addr, u8 *data, u8 *size) +{ + struct gm200_i2c_aux *aux = gm200_i2c_aux(obj); + struct nvkm_i2c *i2c = aux->base.pad->i2c; + struct nvkm_device *device = i2c->subdev.device; + const u32 base = aux->ch * 0x50; + u32 ctrl, stat, timeout, retries = 0; + u32 xbuf[4] = {}; + int ret, i; + + AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, *size); + + ret = gm200_i2c_aux_init(aux); + if (ret < 0) + goto out; + + stat = nvkm_rd32(device, 0x00d958 + base); + if (!(stat & 0x10000000)) { + AUX_TRACE(&aux->base, "sink not detected"); + ret = -ENXIO; + goto out; + } + + nvkm_i2c_aux_autodpcd(i2c, aux->ch, false); + + if (!(type & 1)) { + memcpy(xbuf, data, *size); + for (i = 0; i < 16; i += 4) { + AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]); + nvkm_wr32(device, 0x00d930 + base + i, xbuf[i / 4]); + } + } + + ctrl = nvkm_rd32(device, 0x00d954 + base); + ctrl &= ~0x0001f1ff; + ctrl |= type << 12; + ctrl |= (*size ? (*size - 1) : 0x00000100); + nvkm_wr32(device, 0x00d950 + base, addr); + + /* (maybe) retry transaction a number of times on failure... */ + do { + /* reset, and delay a while if this is a retry */ + nvkm_wr32(device, 0x00d954 + base, 0x80000000 | ctrl); + nvkm_wr32(device, 0x00d954 + base, 0x00000000 | ctrl); + if (retries) + udelay(400); + + /* transaction request, wait up to 2ms for it to complete */ + nvkm_wr32(device, 0x00d954 + base, 0x00010000 | ctrl); + + timeout = 2000; + do { + ctrl = nvkm_rd32(device, 0x00d954 + base); + udelay(1); + if (!timeout--) { + AUX_ERR(&aux->base, "timeout %08x", ctrl); + ret = -EIO; + goto out_err; + } + } while (ctrl & 0x00010000); + ret = 0; + + /* read status, and check if transaction completed ok */ + stat = nvkm_mask(device, 0x00d958 + base, 0, 0); + if ((stat & 0x000f0000) == 0x00080000 || + (stat & 0x000f0000) == 0x00020000) + ret = 1; + if ((stat & 0x00000100)) + ret = -ETIMEDOUT; + if ((stat & 0x00000e00)) + ret = -EIO; + + AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat); + } while (ret && retry && retries++ < 32); + + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nvkm_rd32(device, 0x00d940 + base + i); + AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]); + } + memcpy(data, xbuf, *size); + *size = stat & 0x0000001f; + } + +out_err: + nvkm_i2c_aux_autodpcd(i2c, aux->ch, true); +out: + gm200_i2c_aux_fini(aux); + return ret < 0 ? ret : (stat & 0x000f0000) >> 16; +} + +static const struct nvkm_i2c_aux_func +gm200_i2c_aux_func = { + .address_only = true, + .xfer = gm200_i2c_aux_xfer, +}; + +int +gm200_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, + struct nvkm_i2c_aux **paux) +{ + struct gm200_i2c_aux *aux; + + if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL))) + return -ENOMEM; + *paux = &aux->base; + + nvkm_i2c_aux_ctor(&gm200_i2c_aux_func, pad, index, &aux->base); + aux->ch = drive; + aux->base.intr = 1 << aux->ch; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c new file mode 100644 index 000000000..cb5cb533d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c @@ -0,0 +1,431 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "aux.h" +#include "bus.h" +#include "pad.h" + +#include +#include +#include +#include +#include + +static struct nvkm_i2c_pad * +nvkm_i2c_pad_find(struct nvkm_i2c *i2c, int id) +{ + struct nvkm_i2c_pad *pad; + + list_for_each_entry(pad, &i2c->pad, head) { + if (pad->id == id) + return pad; + } + + return NULL; +} + +struct nvkm_i2c_bus * +nvkm_i2c_bus_find(struct nvkm_i2c *i2c, int id) +{ + struct nvkm_bios *bios = i2c->subdev.device->bios; + struct nvkm_i2c_bus *bus; + + if (id == NVKM_I2C_BUS_PRI || id == NVKM_I2C_BUS_SEC) { + u8 ver, hdr, cnt, len; + u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len); + if (i2c && ver >= 0x30) { + u8 auxidx = nvbios_rd08(bios, i2c + 4); + if (id == NVKM_I2C_BUS_PRI) + id = NVKM_I2C_BUS_CCB((auxidx & 0x0f) >> 0); + else + id = NVKM_I2C_BUS_CCB((auxidx & 0xf0) >> 4); + } else { + id = NVKM_I2C_BUS_CCB(2); + } + } + + list_for_each_entry(bus, &i2c->bus, head) { + if (bus->id == id) + return bus; + } + + return NULL; +} + +struct nvkm_i2c_aux * +nvkm_i2c_aux_find(struct nvkm_i2c *i2c, int id) +{ + struct nvkm_i2c_aux *aux; + + list_for_each_entry(aux, &i2c->aux, head) { + if (aux->id == id) + return aux; + } + + return NULL; +} + +static void +nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int id) +{ + struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event); + struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id); + if (aux) + i2c->func->aux_mask(i2c, type, aux->intr, 0); +} + +static void +nvkm_i2c_intr_init(struct nvkm_event *event, int type, int id) +{ + struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event); + struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id); + if (aux) + i2c->func->aux_mask(i2c, type, aux->intr, aux->intr); +} + +static int +nvkm_i2c_intr_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nvkm_i2c_ntfy_req *req = data; + if (!WARN_ON(size != sizeof(*req))) { + notify->size = sizeof(struct nvkm_i2c_ntfy_rep); + notify->types = req->mask; + notify->index = req->port; + return 0; + } + return -EINVAL; +} + +static const struct nvkm_event_func +nvkm_i2c_intr_func = { + .ctor = nvkm_i2c_intr_ctor, + .init = nvkm_i2c_intr_init, + .fini = nvkm_i2c_intr_fini, +}; + +static void +nvkm_i2c_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_i2c *i2c = nvkm_i2c(subdev); + struct nvkm_i2c_aux *aux; + u32 hi, lo, rq, tx; + + if (!i2c->func->aux_stat) + return; + + i2c->func->aux_stat(i2c, &hi, &lo, &rq, &tx); + if (!hi && !lo && !rq && !tx) + return; + + list_for_each_entry(aux, &i2c->aux, head) { + u32 mask = 0; + if (hi & aux->intr) mask |= NVKM_I2C_PLUG; + if (lo & aux->intr) mask |= NVKM_I2C_UNPLUG; + if (rq & aux->intr) mask |= NVKM_I2C_IRQ; + if (tx & aux->intr) mask |= NVKM_I2C_DONE; + if (mask) { + struct nvkm_i2c_ntfy_rep rep = { + .mask = mask, + }; + nvkm_event_send(&i2c->event, rep.mask, aux->id, + &rep, sizeof(rep)); + } + } +} + +static int +nvkm_i2c_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_i2c *i2c = nvkm_i2c(subdev); + struct nvkm_i2c_pad *pad; + struct nvkm_i2c_bus *bus; + struct nvkm_i2c_aux *aux; + u32 mask; + + list_for_each_entry(aux, &i2c->aux, head) { + nvkm_i2c_aux_fini(aux); + } + + list_for_each_entry(bus, &i2c->bus, head) { + nvkm_i2c_bus_fini(bus); + } + + if ((mask = (1 << i2c->func->aux) - 1), i2c->func->aux_stat) { + i2c->func->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); + i2c->func->aux_stat(i2c, &mask, &mask, &mask, &mask); + } + + list_for_each_entry(pad, &i2c->pad, head) { + nvkm_i2c_pad_fini(pad); + } + + return 0; +} + +static int +nvkm_i2c_preinit(struct nvkm_subdev *subdev) +{ + struct nvkm_i2c *i2c = nvkm_i2c(subdev); + struct nvkm_i2c_bus *bus; + struct nvkm_i2c_pad *pad; + + /* + * We init our i2c busses as early as possible, since they may be + * needed by the vbios init scripts on some cards + */ + list_for_each_entry(pad, &i2c->pad, head) + nvkm_i2c_pad_init(pad); + list_for_each_entry(bus, &i2c->bus, head) + nvkm_i2c_bus_init(bus); + + return 0; +} + +static int +nvkm_i2c_init(struct nvkm_subdev *subdev) +{ + struct nvkm_i2c *i2c = nvkm_i2c(subdev); + struct nvkm_i2c_bus *bus; + struct nvkm_i2c_pad *pad; + struct nvkm_i2c_aux *aux; + + list_for_each_entry(pad, &i2c->pad, head) { + nvkm_i2c_pad_init(pad); + } + + list_for_each_entry(bus, &i2c->bus, head) { + nvkm_i2c_bus_init(bus); + } + + list_for_each_entry(aux, &i2c->aux, head) { + nvkm_i2c_aux_init(aux); + } + + return 0; +} + +static void * +nvkm_i2c_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_i2c *i2c = nvkm_i2c(subdev); + + nvkm_event_fini(&i2c->event); + + while (!list_empty(&i2c->aux)) { + struct nvkm_i2c_aux *aux = + list_first_entry(&i2c->aux, typeof(*aux), head); + nvkm_i2c_aux_del(&aux); + } + + while (!list_empty(&i2c->bus)) { + struct nvkm_i2c_bus *bus = + list_first_entry(&i2c->bus, typeof(*bus), head); + nvkm_i2c_bus_del(&bus); + } + + while (!list_empty(&i2c->pad)) { + struct nvkm_i2c_pad *pad = + list_first_entry(&i2c->pad, typeof(*pad), head); + nvkm_i2c_pad_del(&pad); + } + + return i2c; +} + +static const struct nvkm_subdev_func +nvkm_i2c = { + .dtor = nvkm_i2c_dtor, + .preinit = nvkm_i2c_preinit, + .init = nvkm_i2c_init, + .fini = nvkm_i2c_fini, + .intr = nvkm_i2c_intr, +}; + +static const struct nvkm_i2c_drv { + u8 bios; + u8 addr; + int (*pad_new)(struct nvkm_i2c_bus *, int id, u8 addr, + struct nvkm_i2c_pad **); +} +nvkm_i2c_drv[] = { + { 0x0d, 0x39, anx9805_pad_new }, + { 0x0e, 0x3b, anx9805_pad_new }, + {} +}; + +int +nvkm_i2c_new_(const struct nvkm_i2c_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_i2c **pi2c) +{ + struct nvkm_bios *bios = device->bios; + struct nvkm_i2c *i2c; + struct dcb_i2c_entry ccbE; + struct dcb_output dcbE; + u8 ver, hdr; + int ret, i; + + if (!(i2c = *pi2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) + return -ENOMEM; + + nvkm_subdev_ctor(&nvkm_i2c, device, type, inst, &i2c->subdev); + i2c->func = func; + INIT_LIST_HEAD(&i2c->pad); + INIT_LIST_HEAD(&i2c->bus); + INIT_LIST_HEAD(&i2c->aux); + + i = -1; + while (!dcb_i2c_parse(bios, ++i, &ccbE)) { + struct nvkm_i2c_pad *pad = NULL; + struct nvkm_i2c_bus *bus = NULL; + struct nvkm_i2c_aux *aux = NULL; + + nvkm_debug(&i2c->subdev, "ccb %02x: type %02x drive %02x " + "sense %02x share %02x auxch %02x\n", i, ccbE.type, + ccbE.drive, ccbE.sense, ccbE.share, ccbE.auxch); + + if (ccbE.share != DCB_I2C_UNUSED) { + const int id = NVKM_I2C_PAD_HYBRID(ccbE.share); + if (!(pad = nvkm_i2c_pad_find(i2c, id))) + ret = func->pad_s_new(i2c, id, &pad); + else + ret = 0; + } else { + ret = func->pad_x_new(i2c, NVKM_I2C_PAD_CCB(i), &pad); + } + + if (ret) { + nvkm_error(&i2c->subdev, "ccb %02x pad, %d\n", i, ret); + nvkm_i2c_pad_del(&pad); + continue; + } + + if (pad->func->bus_new_0 && ccbE.type == DCB_I2C_NV04_BIT) { + ret = pad->func->bus_new_0(pad, NVKM_I2C_BUS_CCB(i), + ccbE.drive, + ccbE.sense, &bus); + } else + if (pad->func->bus_new_4 && + ( ccbE.type == DCB_I2C_NV4E_BIT || + ccbE.type == DCB_I2C_NVIO_BIT || + (ccbE.type == DCB_I2C_PMGR && + ccbE.drive != DCB_I2C_UNUSED))) { + ret = pad->func->bus_new_4(pad, NVKM_I2C_BUS_CCB(i), + ccbE.drive, &bus); + } + + if (ret) { + nvkm_error(&i2c->subdev, "ccb %02x bus, %d\n", i, ret); + nvkm_i2c_bus_del(&bus); + } + + if (pad->func->aux_new_6 && + ( ccbE.type == DCB_I2C_NVIO_AUX || + (ccbE.type == DCB_I2C_PMGR && + ccbE.auxch != DCB_I2C_UNUSED))) { + ret = pad->func->aux_new_6(pad, NVKM_I2C_BUS_CCB(i), + ccbE.auxch, &aux); + } else { + ret = 0; + } + + if (ret) { + nvkm_error(&i2c->subdev, "ccb %02x aux, %d\n", i, ret); + nvkm_i2c_aux_del(&aux); + } + + if (ccbE.type != DCB_I2C_UNUSED && !bus && !aux) { + nvkm_warn(&i2c->subdev, "ccb %02x was ignored\n", i); + continue; + } + } + + i = -1; + while (dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE)) { + const struct nvkm_i2c_drv *drv = nvkm_i2c_drv; + struct nvkm_i2c_bus *bus; + struct nvkm_i2c_pad *pad; + + /* internal outputs handled by native i2c busses (above) */ + if (!dcbE.location) + continue; + + /* we need an i2c bus to talk to the external encoder */ + bus = nvkm_i2c_bus_find(i2c, dcbE.i2c_index); + if (!bus) { + nvkm_debug(&i2c->subdev, "dcb %02x no bus\n", i); + continue; + } + + /* ... and a driver for it */ + while (drv->pad_new) { + if (drv->bios == dcbE.extdev) + break; + drv++; + } + + if (!drv->pad_new) { + nvkm_debug(&i2c->subdev, "dcb %02x drv %02x unknown\n", + i, dcbE.extdev); + continue; + } + + /* find/create an instance of the driver */ + pad = nvkm_i2c_pad_find(i2c, NVKM_I2C_PAD_EXT(dcbE.extdev)); + if (!pad) { + const int id = NVKM_I2C_PAD_EXT(dcbE.extdev); + ret = drv->pad_new(bus, id, drv->addr, &pad); + if (ret) { + nvkm_error(&i2c->subdev, "dcb %02x pad, %d\n", + i, ret); + nvkm_i2c_pad_del(&pad); + continue; + } + } + + /* create any i2c bus / aux channel required by the output */ + if (pad->func->aux_new_6 && dcbE.type == DCB_OUTPUT_DP) { + const int id = NVKM_I2C_AUX_EXT(dcbE.extdev); + struct nvkm_i2c_aux *aux = NULL; + ret = pad->func->aux_new_6(pad, id, 0, &aux); + if (ret) { + nvkm_error(&i2c->subdev, "dcb %02x aux, %d\n", + i, ret); + nvkm_i2c_aux_del(&aux); + } + } else + if (pad->func->bus_new_4) { + const int id = NVKM_I2C_BUS_EXT(dcbE.extdev); + struct nvkm_i2c_bus *bus = NULL; + ret = pad->func->bus_new_4(pad, id, 0, &bus); + if (ret) { + nvkm_error(&i2c->subdev, "dcb %02x bus, %d\n", + i, ret); + nvkm_i2c_bus_del(&bus); + } + } + } + + return nvkm_event_init(&nvkm_i2c_intr_func, 4, i, &i2c->event); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c new file mode 100644 index 000000000..cdce11bba --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c @@ -0,0 +1,216 @@ +/* + * 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 busions 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 "bus.h" + +#ifdef CONFIG_NOUVEAU_I2C_INTERNAL +#define T_TIMEOUT 2200000 +#define T_RISEFALL 1000 +#define T_HOLD 5000 + +static inline void +nvkm_i2c_drive_scl(struct nvkm_i2c_bus *bus, int state) +{ + bus->func->drive_scl(bus, state); +} + +static inline void +nvkm_i2c_drive_sda(struct nvkm_i2c_bus *bus, int state) +{ + bus->func->drive_sda(bus, state); +} + +static inline int +nvkm_i2c_sense_scl(struct nvkm_i2c_bus *bus) +{ + return bus->func->sense_scl(bus); +} + +static inline int +nvkm_i2c_sense_sda(struct nvkm_i2c_bus *bus) +{ + return bus->func->sense_sda(bus); +} + +static void +nvkm_i2c_delay(struct nvkm_i2c_bus *bus, u32 nsec) +{ + udelay((nsec + 500) / 1000); +} + +static bool +nvkm_i2c_raise_scl(struct nvkm_i2c_bus *bus) +{ + u32 timeout = T_TIMEOUT / T_RISEFALL; + + nvkm_i2c_drive_scl(bus, 1); + do { + nvkm_i2c_delay(bus, T_RISEFALL); + } while (!nvkm_i2c_sense_scl(bus) && --timeout); + + return timeout != 0; +} + +static int +i2c_start(struct nvkm_i2c_bus *bus) +{ + int ret = 0; + + if (!nvkm_i2c_sense_scl(bus) || + !nvkm_i2c_sense_sda(bus)) { + nvkm_i2c_drive_scl(bus, 0); + nvkm_i2c_drive_sda(bus, 1); + if (!nvkm_i2c_raise_scl(bus)) + ret = -EBUSY; + } + + nvkm_i2c_drive_sda(bus, 0); + nvkm_i2c_delay(bus, T_HOLD); + nvkm_i2c_drive_scl(bus, 0); + nvkm_i2c_delay(bus, T_HOLD); + return ret; +} + +static void +i2c_stop(struct nvkm_i2c_bus *bus) +{ + nvkm_i2c_drive_scl(bus, 0); + nvkm_i2c_drive_sda(bus, 0); + nvkm_i2c_delay(bus, T_RISEFALL); + + nvkm_i2c_drive_scl(bus, 1); + nvkm_i2c_delay(bus, T_HOLD); + nvkm_i2c_drive_sda(bus, 1); + nvkm_i2c_delay(bus, T_HOLD); +} + +static int +i2c_bitw(struct nvkm_i2c_bus *bus, int sda) +{ + nvkm_i2c_drive_sda(bus, sda); + nvkm_i2c_delay(bus, T_RISEFALL); + + if (!nvkm_i2c_raise_scl(bus)) + return -ETIMEDOUT; + nvkm_i2c_delay(bus, T_HOLD); + + nvkm_i2c_drive_scl(bus, 0); + nvkm_i2c_delay(bus, T_HOLD); + return 0; +} + +static int +i2c_bitr(struct nvkm_i2c_bus *bus) +{ + int sda; + + nvkm_i2c_drive_sda(bus, 1); + nvkm_i2c_delay(bus, T_RISEFALL); + + if (!nvkm_i2c_raise_scl(bus)) + return -ETIMEDOUT; + nvkm_i2c_delay(bus, T_HOLD); + + sda = nvkm_i2c_sense_sda(bus); + + nvkm_i2c_drive_scl(bus, 0); + nvkm_i2c_delay(bus, T_HOLD); + return sda; +} + +static int +nvkm_i2c_get_byte(struct nvkm_i2c_bus *bus, u8 *byte, bool last) +{ + int i, bit; + + *byte = 0; + for (i = 7; i >= 0; i--) { + bit = i2c_bitr(bus); + if (bit < 0) + return bit; + *byte |= bit << i; + } + + return i2c_bitw(bus, last ? 1 : 0); +} + +static int +nvkm_i2c_put_byte(struct nvkm_i2c_bus *bus, u8 byte) +{ + int i, ret; + for (i = 7; i >= 0; i--) { + ret = i2c_bitw(bus, !!(byte & (1 << i))); + if (ret < 0) + return ret; + } + + ret = i2c_bitr(bus); + if (ret == 1) /* nack */ + ret = -EIO; + return ret; +} + +static int +i2c_addr(struct nvkm_i2c_bus *bus, struct i2c_msg *msg) +{ + u32 addr = msg->addr << 1; + if (msg->flags & I2C_M_RD) + addr |= 1; + return nvkm_i2c_put_byte(bus, addr); +} + +int +nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, struct i2c_msg *msgs, int num) +{ + struct i2c_msg *msg = msgs; + int ret = 0, mcnt = num; + + while (!ret && mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; + + ret = i2c_start(bus); + if (ret == 0) + ret = i2c_addr(bus, msg); + + if (msg->flags & I2C_M_RD) { + while (!ret && remaining--) + ret = nvkm_i2c_get_byte(bus, ptr++, !remaining); + } else { + while (!ret && remaining--) + ret = nvkm_i2c_put_byte(bus, *ptr++); + } + + msg++; + } + + i2c_stop(bus); + return (ret < 0) ? ret : num; +} +#else +int +nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, struct i2c_msg *msgs, int num) +{ + return -ENODEV; +} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c new file mode 100644 index 000000000..ed50cc373 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c @@ -0,0 +1,264 @@ +/* + * Copyright 2015 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 "bus.h" +#include "pad.h" + +#include + +/******************************************************************************* + * i2c-algo-bit + ******************************************************************************/ +static int +nvkm_i2c_bus_pre_xfer(struct i2c_adapter *adap) +{ + struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); + return nvkm_i2c_bus_acquire(bus); +} + +static void +nvkm_i2c_bus_post_xfer(struct i2c_adapter *adap) +{ + struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); + return nvkm_i2c_bus_release(bus); +} + +static void +nvkm_i2c_bus_setscl(void *data, int state) +{ + struct nvkm_i2c_bus *bus = data; + bus->func->drive_scl(bus, state); +} + +static void +nvkm_i2c_bus_setsda(void *data, int state) +{ + struct nvkm_i2c_bus *bus = data; + bus->func->drive_sda(bus, state); +} + +static int +nvkm_i2c_bus_getscl(void *data) +{ + struct nvkm_i2c_bus *bus = data; + return bus->func->sense_scl(bus); +} + +static int +nvkm_i2c_bus_getsda(void *data) +{ + struct nvkm_i2c_bus *bus = data; + return bus->func->sense_sda(bus); +} + +/******************************************************************************* + * !i2c-algo-bit (off-chip i2c bus / hw i2c / internal bit-banging algo) + ******************************************************************************/ +static int +nvkm_i2c_bus_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); + int ret; + + ret = nvkm_i2c_bus_acquire(bus); + if (ret) + return ret; + + ret = bus->func->xfer(bus, msgs, num); + nvkm_i2c_bus_release(bus); + return ret; +} + +static u32 +nvkm_i2c_bus_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm +nvkm_i2c_bus_algo = { + .master_xfer = nvkm_i2c_bus_xfer, + .functionality = nvkm_i2c_bus_func, +}; + +/******************************************************************************* + * nvkm_i2c_bus base + ******************************************************************************/ +void +nvkm_i2c_bus_init(struct nvkm_i2c_bus *bus) +{ + BUS_TRACE(bus, "init"); + if (bus->func->init) + bus->func->init(bus); + + mutex_lock(&bus->mutex); + bus->enabled = true; + mutex_unlock(&bus->mutex); +} + +void +nvkm_i2c_bus_fini(struct nvkm_i2c_bus *bus) +{ + BUS_TRACE(bus, "fini"); + mutex_lock(&bus->mutex); + bus->enabled = false; + mutex_unlock(&bus->mutex); +} + +void +nvkm_i2c_bus_release(struct nvkm_i2c_bus *bus) +{ + struct nvkm_i2c_pad *pad = bus->pad; + BUS_TRACE(bus, "release"); + nvkm_i2c_pad_release(pad); + mutex_unlock(&bus->mutex); +} + +int +nvkm_i2c_bus_acquire(struct nvkm_i2c_bus *bus) +{ + struct nvkm_i2c_pad *pad = bus->pad; + int ret; + + BUS_TRACE(bus, "acquire"); + mutex_lock(&bus->mutex); + + if (bus->enabled) + ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_I2C); + else + ret = -EIO; + + if (ret) + mutex_unlock(&bus->mutex); + return ret; +} + +int +nvkm_i2c_bus_probe(struct nvkm_i2c_bus *bus, const char *what, + struct nvkm_i2c_bus_probe *info, + bool (*match)(struct nvkm_i2c_bus *, + struct i2c_board_info *, void *), void *data) +{ + int i; + + BUS_DBG(bus, "probing %ss", what); + for (i = 0; info[i].dev.addr; i++) { + u8 orig_udelay = 0; + + if ((bus->i2c.algo == &i2c_bit_algo) && (info[i].udelay != 0)) { + struct i2c_algo_bit_data *algo = bus->i2c.algo_data; + BUS_DBG(bus, "%dms delay instead of %dms", + info[i].udelay, algo->udelay); + orig_udelay = algo->udelay; + algo->udelay = info[i].udelay; + } + + if (nvkm_probe_i2c(&bus->i2c, info[i].dev.addr) && + (!match || match(bus, &info[i].dev, data))) { + BUS_DBG(bus, "detected %s: %s", + what, info[i].dev.type); + return i; + } + + if (orig_udelay) { + struct i2c_algo_bit_data *algo = bus->i2c.algo_data; + algo->udelay = orig_udelay; + } + } + + BUS_DBG(bus, "no devices found."); + return -ENODEV; +} + +void +nvkm_i2c_bus_del(struct nvkm_i2c_bus **pbus) +{ + struct nvkm_i2c_bus *bus = *pbus; + if (bus && !WARN_ON(!bus->func)) { + BUS_TRACE(bus, "dtor"); + list_del(&bus->head); + i2c_del_adapter(&bus->i2c); + kfree(bus->i2c.algo_data); + kfree(*pbus); + *pbus = NULL; + } +} + +int +nvkm_i2c_bus_ctor(const struct nvkm_i2c_bus_func *func, + struct nvkm_i2c_pad *pad, int id, + struct nvkm_i2c_bus *bus) +{ + struct nvkm_device *device = pad->i2c->subdev.device; + struct i2c_algo_bit_data *bit; +#ifndef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT + const bool internal = false; +#else + const bool internal = true; +#endif + int ret; + + bus->func = func; + bus->pad = pad; + bus->id = id; + mutex_init(&bus->mutex); + list_add_tail(&bus->head, &pad->i2c->bus); + BUS_TRACE(bus, "ctor"); + + snprintf(bus->i2c.name, sizeof(bus->i2c.name), "nvkm-%s-bus-%04x", + dev_name(device->dev), id); + bus->i2c.owner = THIS_MODULE; + bus->i2c.dev.parent = device->dev; + + if ( bus->func->drive_scl && + !nvkm_boolopt(device->cfgopt, "NvI2C", internal)) { + if (!(bit = kzalloc(sizeof(*bit), GFP_KERNEL))) + return -ENOMEM; + bit->udelay = 10; + bit->timeout = usecs_to_jiffies(2200); + bit->data = bus; + bit->pre_xfer = nvkm_i2c_bus_pre_xfer; + bit->post_xfer = nvkm_i2c_bus_post_xfer; + bit->setscl = nvkm_i2c_bus_setscl; + bit->setsda = nvkm_i2c_bus_setsda; + bit->getscl = nvkm_i2c_bus_getscl; + bit->getsda = nvkm_i2c_bus_getsda; + bus->i2c.algo_data = bit; + ret = i2c_bit_add_bus(&bus->i2c); + } else { + bus->i2c.algo = &nvkm_i2c_bus_algo; + ret = i2c_add_adapter(&bus->i2c); + } + + return ret; +} + +int +nvkm_i2c_bus_new_(const struct nvkm_i2c_bus_func *func, + struct nvkm_i2c_pad *pad, int id, + struct nvkm_i2c_bus **pbus) +{ + if (!(*pbus = kzalloc(sizeof(**pbus), GFP_KERNEL))) + return -ENOMEM; + return nvkm_i2c_bus_ctor(func, pad, id, *pbus); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h new file mode 100644 index 000000000..4c236ab34 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_I2C_BUS_H__ +#define __NVKM_I2C_BUS_H__ +#include "pad.h" + +struct nvkm_i2c_bus_func { + void (*init)(struct nvkm_i2c_bus *); + void (*drive_scl)(struct nvkm_i2c_bus *, int state); + void (*drive_sda)(struct nvkm_i2c_bus *, int state); + int (*sense_scl)(struct nvkm_i2c_bus *); + int (*sense_sda)(struct nvkm_i2c_bus *); + int (*xfer)(struct nvkm_i2c_bus *, struct i2c_msg *, int num); +}; + +int nvkm_i2c_bus_ctor(const struct nvkm_i2c_bus_func *, struct nvkm_i2c_pad *, + int id, struct nvkm_i2c_bus *); +int nvkm_i2c_bus_new_(const struct nvkm_i2c_bus_func *, struct nvkm_i2c_pad *, + int id, struct nvkm_i2c_bus **); +void nvkm_i2c_bus_del(struct nvkm_i2c_bus **); +void nvkm_i2c_bus_init(struct nvkm_i2c_bus *); +void nvkm_i2c_bus_fini(struct nvkm_i2c_bus *); + +int nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *, struct i2c_msg *, int); + +int nv04_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, u8, + struct nvkm_i2c_bus **); + +int nv4e_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_bus **); +int nv50_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_bus **); +int gf119_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_bus **); + +#define BUS_MSG(b,l,f,a...) do { \ + struct nvkm_i2c_bus *_bus = (b); \ + nvkm_##l(&_bus->pad->i2c->subdev, "bus %04x: "f"\n", _bus->id, ##a); \ +} while(0) +#define BUS_ERR(b,f,a...) BUS_MSG((b), error, f, ##a) +#define BUS_DBG(b,f,a...) BUS_MSG((b), debug, f, ##a) +#define BUS_TRACE(b,f,a...) BUS_MSG((b), trace, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c new file mode 100644 index 000000000..96bbdda0f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c @@ -0,0 +1,95 @@ +/* + * Copyright 2015 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 busions 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 + */ +#define gf119_i2c_bus(p) container_of((p), struct gf119_i2c_bus, base) +#include "bus.h" + +struct gf119_i2c_bus { + struct nvkm_i2c_bus base; + u32 addr; +}; + +static void +gf119_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ + struct gf119_i2c_bus *bus = gf119_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + nvkm_mask(device, bus->addr, 0x00000001, state ? 0x00000001 : 0); +} + +static void +gf119_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ + struct gf119_i2c_bus *bus = gf119_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + nvkm_mask(device, bus->addr, 0x00000002, state ? 0x00000002 : 0); +} + +static int +gf119_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ + struct gf119_i2c_bus *bus = gf119_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rd32(device, bus->addr) & 0x00000010); +} + +static int +gf119_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ + struct gf119_i2c_bus *bus = gf119_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rd32(device, bus->addr) & 0x00000020); +} + +static void +gf119_i2c_bus_init(struct nvkm_i2c_bus *base) +{ + struct gf119_i2c_bus *bus = gf119_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + nvkm_wr32(device, bus->addr, 0x00000007); +} + +static const struct nvkm_i2c_bus_func +gf119_i2c_bus_func = { + .init = gf119_i2c_bus_init, + .drive_scl = gf119_i2c_bus_drive_scl, + .drive_sda = gf119_i2c_bus_drive_sda, + .sense_scl = gf119_i2c_bus_sense_scl, + .sense_sda = gf119_i2c_bus_sense_sda, + .xfer = nvkm_i2c_bit_xfer, +}; + +int +gf119_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, + struct nvkm_i2c_bus **pbus) +{ + struct gf119_i2c_bus *bus; + + if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) + return -ENOMEM; + *pbus = &bus->base; + + nvkm_i2c_bus_ctor(&gf119_i2c_bus_func, pad, id, &bus->base); + bus->addr = 0x00d014 + (drive * 0x20); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c new file mode 100644 index 000000000..a58db1592 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c @@ -0,0 +1,96 @@ +/* + * Copyright 2015 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 busions 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 + */ +#define nv04_i2c_bus(p) container_of((p), struct nv04_i2c_bus, base) +#include "bus.h" + +#include + +struct nv04_i2c_bus { + struct nvkm_i2c_bus base; + u8 drive; + u8 sense; +}; + +static void +nv04_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ + struct nv04_i2c_bus *bus = nv04_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + u8 val = nvkm_rdvgac(device, 0, bus->drive); + if (state) val |= 0x20; + else val &= 0xdf; + nvkm_wrvgac(device, 0, bus->drive, val | 0x01); +} + +static void +nv04_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ + struct nv04_i2c_bus *bus = nv04_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + u8 val = nvkm_rdvgac(device, 0, bus->drive); + if (state) val |= 0x10; + else val &= 0xef; + nvkm_wrvgac(device, 0, bus->drive, val | 0x01); +} + +static int +nv04_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ + struct nv04_i2c_bus *bus = nv04_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rdvgac(device, 0, bus->sense) & 0x04); +} + +static int +nv04_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ + struct nv04_i2c_bus *bus = nv04_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rdvgac(device, 0, bus->sense) & 0x08); +} + +static const struct nvkm_i2c_bus_func +nv04_i2c_bus_func = { + .drive_scl = nv04_i2c_bus_drive_scl, + .drive_sda = nv04_i2c_bus_drive_sda, + .sense_scl = nv04_i2c_bus_sense_scl, + .sense_sda = nv04_i2c_bus_sense_sda, + .xfer = nvkm_i2c_bit_xfer, +}; + +int +nv04_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, u8 sense, + struct nvkm_i2c_bus **pbus) +{ + struct nv04_i2c_bus *bus; + + if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) + return -ENOMEM; + *pbus = &bus->base; + + nvkm_i2c_bus_ctor(&nv04_i2c_bus_func, pad, id, &bus->base); + bus->drive = drive; + bus->sense = sense; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c new file mode 100644 index 000000000..cdd73dcb1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c @@ -0,0 +1,86 @@ +/* + * Copyright 2015 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 busions 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 + */ +#define nv4e_i2c_bus(p) container_of((p), struct nv4e_i2c_bus, base) +#include "bus.h" + +struct nv4e_i2c_bus { + struct nvkm_i2c_bus base; + u32 addr; +}; + +static void +nv4e_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ + struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + nvkm_mask(device, bus->addr, 0x2f, state ? 0x21 : 0x01); +} + +static void +nv4e_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ + struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + nvkm_mask(device, bus->addr, 0x1f, state ? 0x11 : 0x01); +} + +static int +nv4e_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ + struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rd32(device, bus->addr) & 0x00040000); +} + +static int +nv4e_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ + struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rd32(device, bus->addr) & 0x00080000); +} + +static const struct nvkm_i2c_bus_func +nv4e_i2c_bus_func = { + .drive_scl = nv4e_i2c_bus_drive_scl, + .drive_sda = nv4e_i2c_bus_drive_sda, + .sense_scl = nv4e_i2c_bus_sense_scl, + .sense_sda = nv4e_i2c_bus_sense_sda, + .xfer = nvkm_i2c_bit_xfer, +}; + +int +nv4e_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, + struct nvkm_i2c_bus **pbus) +{ + struct nv4e_i2c_bus *bus; + + if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) + return -ENOMEM; + *pbus = &bus->base; + + nvkm_i2c_bus_ctor(&nv4e_i2c_bus_func, pad, id, &bus->base); + bus->addr = 0x600800 + drive; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c new file mode 100644 index 000000000..8db839938 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c @@ -0,0 +1,113 @@ +/* + * Copyright 2015 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 busions 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 + */ +#define nv50_i2c_bus(p) container_of((p), struct nv50_i2c_bus, base) +#include "bus.h" + +#include + +struct nv50_i2c_bus { + struct nvkm_i2c_bus base; + u32 addr; + u32 data; +}; + +static void +nv50_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ + struct nv50_i2c_bus *bus = nv50_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + if (state) bus->data |= 0x01; + else bus->data &= 0xfe; + nvkm_wr32(device, bus->addr, bus->data); +} + +static void +nv50_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ + struct nv50_i2c_bus *bus = nv50_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + if (state) bus->data |= 0x02; + else bus->data &= 0xfd; + nvkm_wr32(device, bus->addr, bus->data); +} + +static int +nv50_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ + struct nv50_i2c_bus *bus = nv50_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rd32(device, bus->addr) & 0x00000001); +} + +static int +nv50_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ + struct nv50_i2c_bus *bus = nv50_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + return !!(nvkm_rd32(device, bus->addr) & 0x00000002); +} + +static void +nv50_i2c_bus_init(struct nvkm_i2c_bus *base) +{ + struct nv50_i2c_bus *bus = nv50_i2c_bus(base); + struct nvkm_device *device = bus->base.pad->i2c->subdev.device; + nvkm_wr32(device, bus->addr, (bus->data = 0x00000007)); +} + +static const struct nvkm_i2c_bus_func +nv50_i2c_bus_func = { + .init = nv50_i2c_bus_init, + .drive_scl = nv50_i2c_bus_drive_scl, + .drive_sda = nv50_i2c_bus_drive_sda, + .sense_scl = nv50_i2c_bus_sense_scl, + .sense_sda = nv50_i2c_bus_sense_sda, + .xfer = nvkm_i2c_bit_xfer, +}; + +int +nv50_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, + struct nvkm_i2c_bus **pbus) +{ + static const u32 addr[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 + }; + struct nv50_i2c_bus *bus; + + if (drive >= ARRAY_SIZE(addr)) { + nvkm_warn(&pad->i2c->subdev, "bus %d unknown\n", drive); + return -ENODEV; + } + + if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) + return -ENOMEM; + *pbus = &bus->base; + + nvkm_i2c_bus_ctor(&nv50_i2c_bus_func, pad, id, &bus->base); + bus->addr = addr[drive]; + bus->data = 0x00000007; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c new file mode 100644 index 000000000..e5bad085c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c @@ -0,0 +1,73 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +void +g94_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + struct nvkm_device *device = i2c->subdev.device; + u32 intr = nvkm_rd32(device, 0x00e06c); + u32 stat = nvkm_rd32(device, 0x00e068) & intr, i; + for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) { + if ((stat & (1 << (i * 4)))) *hi |= 1 << i; + if ((stat & (2 << (i * 4)))) *lo |= 1 << i; + if ((stat & (4 << (i * 4)))) *rq |= 1 << i; + if ((stat & (8 << (i * 4)))) *tx |= 1 << i; + } + nvkm_wr32(device, 0x00e06c, intr); +} + +void +g94_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data) +{ + struct nvkm_device *device = i2c->subdev.device; + u32 temp = nvkm_rd32(device, 0x00e068), i; + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) { + if (!(data & (1 << i))) { + temp &= ~(type << (i * 4)); + continue; + } + temp |= type << (i * 4); + } + } + nvkm_wr32(device, 0x00e068, temp); +} + +static const struct nvkm_i2c_func +g94_i2c = { + .pad_x_new = g94_i2c_pad_x_new, + .pad_s_new = g94_i2c_pad_s_new, + .aux = 4, + .aux_stat = g94_aux_stat, + .aux_mask = g94_aux_mask, +}; + +int +g94_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&g94_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c new file mode 100644 index 000000000..cda30ee67 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +static const struct nvkm_i2c_func +gf117_i2c = { + .pad_x_new = gf119_i2c_pad_x_new, +}; + +int +gf117_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&gf117_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c new file mode 100644 index 000000000..e9c6a6cca --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +static const struct nvkm_i2c_func +gf119_i2c = { + .pad_x_new = gf119_i2c_pad_x_new, + .pad_s_new = gf119_i2c_pad_s_new, + .aux = 4, + .aux_stat = g94_aux_stat, + .aux_mask = g94_aux_mask, +}; + +int +gf119_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&gf119_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c new file mode 100644 index 000000000..d35aa6fe3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c @@ -0,0 +1,73 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +void +gk104_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + struct nvkm_device *device = i2c->subdev.device; + u32 intr = nvkm_rd32(device, 0x00dc60); + u32 stat = nvkm_rd32(device, 0x00dc68) & intr, i; + for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) { + if ((stat & (1 << (i * 4)))) *hi |= 1 << i; + if ((stat & (2 << (i * 4)))) *lo |= 1 << i; + if ((stat & (4 << (i * 4)))) *rq |= 1 << i; + if ((stat & (8 << (i * 4)))) *tx |= 1 << i; + } + nvkm_wr32(device, 0x00dc60, intr); +} + +void +gk104_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data) +{ + struct nvkm_device *device = i2c->subdev.device; + u32 temp = nvkm_rd32(device, 0x00dc68), i; + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) { + if (!(data & (1 << i))) { + temp &= ~(type << (i * 4)); + continue; + } + temp |= type << (i * 4); + } + } + nvkm_wr32(device, 0x00dc68, temp); +} + +static const struct nvkm_i2c_func +gk104_i2c = { + .pad_x_new = gf119_i2c_pad_x_new, + .pad_s_new = gf119_i2c_pad_s_new, + .aux = 4, + .aux_stat = gk104_aux_stat, + .aux_mask = gk104_aux_mask, +}; + +int +gk104_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&gk104_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c new file mode 100644 index 000000000..9fec6af56 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c @@ -0,0 +1,46 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include "pad.h" + +static void +gk110_aux_autodpcd(struct nvkm_i2c *i2c, int aux, bool enable) +{ + nvkm_mask(i2c->subdev.device, 0x00e4f8 + (aux * 0x50), 0x00010000, enable << 16); +} + +static const struct nvkm_i2c_func +gk110_i2c = { + .pad_x_new = gf119_i2c_pad_x_new, + .pad_s_new = gf119_i2c_pad_s_new, + .aux = 4, + .aux_stat = gk104_aux_stat, + .aux_mask = gk104_aux_mask, + .aux_autodpcd = gk110_aux_autodpcd, +}; + +int +gk110_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&gk110_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm200.c new file mode 100644 index 000000000..46917eb60 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm200.c @@ -0,0 +1,48 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +static void +gm200_aux_autodpcd(struct nvkm_i2c *i2c, int aux, bool enable) +{ + nvkm_mask(i2c->subdev.device, 0x00d968 + (aux * 0x50), 0x00010000, enable << 16); +} + +static const struct nvkm_i2c_func +gm200_i2c = { + .pad_x_new = gf119_i2c_pad_x_new, + .pad_s_new = gm200_i2c_pad_s_new, + .aux = 8, + .aux_stat = gk104_aux_stat, + .aux_mask = gk104_aux_mask, + .aux_autodpcd = gm200_aux_autodpcd, +}; + +int +gm200_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&gm200_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c new file mode 100644 index 000000000..ecfcf147c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +static const struct nvkm_i2c_func +nv04_i2c = { + .pad_x_new = nv04_i2c_pad_new, +}; + +int +nv04_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&nv04_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c new file mode 100644 index 000000000..ad1d3fd2b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +static const struct nvkm_i2c_func +nv4e_i2c = { + .pad_x_new = nv4e_i2c_pad_new, +}; + +int +nv4e_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&nv4e_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c new file mode 100644 index 000000000..2f94bed2c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "pad.h" + +static const struct nvkm_i2c_func +nv50_i2c = { + .pad_x_new = nv50_i2c_pad_new, +}; + +int +nv50_i2c_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_i2c **pi2c) +{ + return nvkm_i2c_new_(&nv50_i2c, device, type, inst, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c new file mode 100644 index 000000000..2c5fcb9c5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c @@ -0,0 +1,116 @@ +/* + * Copyright 2015 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 "pad.h" + +static void +nvkm_i2c_pad_mode_locked(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode) +{ + PAD_TRACE(pad, "-> %s", (mode == NVKM_I2C_PAD_AUX) ? "aux" : + (mode == NVKM_I2C_PAD_I2C) ? "i2c" : "off"); + if (pad->func->mode) + pad->func->mode(pad, mode); +} + +void +nvkm_i2c_pad_mode(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode) +{ + PAD_TRACE(pad, "mode %d", mode); + mutex_lock(&pad->mutex); + nvkm_i2c_pad_mode_locked(pad, mode); + pad->mode = mode; + mutex_unlock(&pad->mutex); +} + +void +nvkm_i2c_pad_release(struct nvkm_i2c_pad *pad) +{ + PAD_TRACE(pad, "release"); + if (pad->mode == NVKM_I2C_PAD_OFF) + nvkm_i2c_pad_mode_locked(pad, pad->mode); + mutex_unlock(&pad->mutex); +} + +int +nvkm_i2c_pad_acquire(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode) +{ + PAD_TRACE(pad, "acquire"); + mutex_lock(&pad->mutex); + if (pad->mode != mode) { + if (pad->mode != NVKM_I2C_PAD_OFF) { + mutex_unlock(&pad->mutex); + return -EBUSY; + } + nvkm_i2c_pad_mode_locked(pad, mode); + } + return 0; +} + +void +nvkm_i2c_pad_fini(struct nvkm_i2c_pad *pad) +{ + PAD_TRACE(pad, "fini"); + nvkm_i2c_pad_mode_locked(pad, NVKM_I2C_PAD_OFF); +} + +void +nvkm_i2c_pad_init(struct nvkm_i2c_pad *pad) +{ + PAD_TRACE(pad, "init"); + nvkm_i2c_pad_mode_locked(pad, pad->mode); +} + +void +nvkm_i2c_pad_del(struct nvkm_i2c_pad **ppad) +{ + struct nvkm_i2c_pad *pad = *ppad; + if (pad) { + PAD_TRACE(pad, "dtor"); + list_del(&pad->head); + kfree(pad); + pad = NULL; + } +} + +void +nvkm_i2c_pad_ctor(const struct nvkm_i2c_pad_func *func, struct nvkm_i2c *i2c, + int id, struct nvkm_i2c_pad *pad) +{ + pad->func = func; + pad->i2c = i2c; + pad->id = id; + pad->mode = NVKM_I2C_PAD_OFF; + mutex_init(&pad->mutex); + list_add_tail(&pad->head, &i2c->pad); + PAD_TRACE(pad, "ctor"); +} + +int +nvkm_i2c_pad_new_(const struct nvkm_i2c_pad_func *func, struct nvkm_i2c *i2c, + int id, struct nvkm_i2c_pad **ppad) +{ + if (!(*ppad = kzalloc(sizeof(**ppad), GFP_KERNEL))) + return -ENOMEM; + nvkm_i2c_pad_ctor(func, i2c, id, *ppad); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h new file mode 100644 index 000000000..44b7bb7d4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_I2C_PAD_H__ +#define __NVKM_I2C_PAD_H__ +#include "priv.h" + +struct nvkm_i2c_pad { + const struct nvkm_i2c_pad_func *func; + struct nvkm_i2c *i2c; +#define NVKM_I2C_PAD_HYBRID(n) /* 'n' is hw pad index */ (n) +#define NVKM_I2C_PAD_CCB(n) /* 'n' is ccb index */ ((n) + 0x100) +#define NVKM_I2C_PAD_EXT(n) /* 'n' is dcb external encoder type */ ((n) + 0x200) + int id; + + enum nvkm_i2c_pad_mode { + NVKM_I2C_PAD_OFF, + NVKM_I2C_PAD_I2C, + NVKM_I2C_PAD_AUX, + } mode; + struct mutex mutex; + struct list_head head; +}; + +struct nvkm_i2c_pad_func { + int (*bus_new_0)(struct nvkm_i2c_pad *, int id, u8 drive, u8 sense, + struct nvkm_i2c_bus **); + int (*bus_new_4)(struct nvkm_i2c_pad *, int id, u8 drive, + struct nvkm_i2c_bus **); + + int (*aux_new_6)(struct nvkm_i2c_pad *, int id, u8 drive, + struct nvkm_i2c_aux **); + + void (*mode)(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode); +}; + +void nvkm_i2c_pad_ctor(const struct nvkm_i2c_pad_func *, struct nvkm_i2c *, + int id, struct nvkm_i2c_pad *); +int nvkm_i2c_pad_new_(const struct nvkm_i2c_pad_func *, struct nvkm_i2c *, + int id, struct nvkm_i2c_pad **); +void nvkm_i2c_pad_del(struct nvkm_i2c_pad **); +void nvkm_i2c_pad_init(struct nvkm_i2c_pad *); +void nvkm_i2c_pad_fini(struct nvkm_i2c_pad *); +void nvkm_i2c_pad_mode(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode); +int nvkm_i2c_pad_acquire(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode); +void nvkm_i2c_pad_release(struct nvkm_i2c_pad *); + +void g94_i2c_pad_mode(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode); + +int nv04_i2c_pad_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int nv4e_i2c_pad_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int nv50_i2c_pad_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int g94_i2c_pad_x_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gf119_i2c_pad_x_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gm200_i2c_pad_x_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); + +int g94_i2c_pad_s_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gf119_i2c_pad_s_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gm200_i2c_pad_s_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); + +int anx9805_pad_new(struct nvkm_i2c_bus *, int, u8, struct nvkm_i2c_pad **); + +#define PAD_MSG(p,l,f,a...) do { \ + struct nvkm_i2c_pad *_pad = (p); \ + nvkm_##l(&_pad->i2c->subdev, "pad %04x: "f"\n", _pad->id, ##a); \ +} while(0) +#define PAD_ERR(p,f,a...) PAD_MSG((p), error, f, ##a) +#define PAD_DBG(p,f,a...) PAD_MSG((p), debug, f, ##a) +#define PAD_TRACE(p,f,a...) PAD_MSG((p), trace, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c new file mode 100644 index 000000000..5904bc5f2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c @@ -0,0 +1,76 @@ +/* + * Copyright 2014 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 "pad.h" +#include "aux.h" +#include "bus.h" + +void +g94_i2c_pad_mode(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode) +{ + struct nvkm_subdev *subdev = &pad->i2c->subdev; + struct nvkm_device *device = subdev->device; + const u32 base = (pad->id - NVKM_I2C_PAD_HYBRID(0)) * 0x50; + + switch (mode) { + case NVKM_I2C_PAD_OFF: + nvkm_mask(device, 0x00e50c + base, 0x00000001, 0x00000001); + break; + case NVKM_I2C_PAD_I2C: + nvkm_mask(device, 0x00e500 + base, 0x0000c003, 0x0000c001); + nvkm_mask(device, 0x00e50c + base, 0x00000001, 0x00000000); + break; + case NVKM_I2C_PAD_AUX: + nvkm_mask(device, 0x00e500 + base, 0x0000c003, 0x00000002); + nvkm_mask(device, 0x00e50c + base, 0x00000001, 0x00000000); + break; + default: + WARN_ON(1); + break; + } +} + +static const struct nvkm_i2c_pad_func +g94_i2c_pad_s_func = { + .bus_new_4 = nv50_i2c_bus_new, + .aux_new_6 = g94_i2c_aux_new, + .mode = g94_i2c_pad_mode, +}; + +int +g94_i2c_pad_s_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&g94_i2c_pad_s_func, i2c, id, ppad); +} + +static const struct nvkm_i2c_pad_func +g94_i2c_pad_x_func = { + .bus_new_4 = nv50_i2c_bus_new, + .aux_new_6 = g94_i2c_aux_new, +}; + +int +g94_i2c_pad_x_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&g94_i2c_pad_x_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c new file mode 100644 index 000000000..3bc4d0310 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c @@ -0,0 +1,51 @@ +/* + * Copyright 2014 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 "pad.h" +#include "aux.h" +#include "bus.h" + +static const struct nvkm_i2c_pad_func +gf119_i2c_pad_s_func = { + .bus_new_4 = gf119_i2c_bus_new, + .aux_new_6 = gf119_i2c_aux_new, + .mode = g94_i2c_pad_mode, +}; + +int +gf119_i2c_pad_s_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&gf119_i2c_pad_s_func, i2c, id, ppad); +} + +static const struct nvkm_i2c_pad_func +gf119_i2c_pad_x_func = { + .bus_new_4 = gf119_i2c_bus_new, + .aux_new_6 = gf119_i2c_aux_new, +}; + +int +gf119_i2c_pad_x_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&gf119_i2c_pad_x_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm200.c new file mode 100644 index 000000000..7d417f6a8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm200.c @@ -0,0 +1,76 @@ +/* + * Copyright 2014 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 "pad.h" +#include "aux.h" +#include "bus.h" + +static void +gm200_i2c_pad_mode(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode) +{ + struct nvkm_subdev *subdev = &pad->i2c->subdev; + struct nvkm_device *device = subdev->device; + const u32 base = (pad->id - NVKM_I2C_PAD_HYBRID(0)) * 0x50; + + switch (mode) { + case NVKM_I2C_PAD_OFF: + nvkm_mask(device, 0x00d97c + base, 0x00000001, 0x00000001); + break; + case NVKM_I2C_PAD_I2C: + nvkm_mask(device, 0x00d970 + base, 0x0000c003, 0x0000c001); + nvkm_mask(device, 0x00d97c + base, 0x00000001, 0x00000000); + break; + case NVKM_I2C_PAD_AUX: + nvkm_mask(device, 0x00d970 + base, 0x0000c003, 0x00000002); + nvkm_mask(device, 0x00d97c + base, 0x00000001, 0x00000000); + break; + default: + WARN_ON(1); + break; + } +} + +static const struct nvkm_i2c_pad_func +gm200_i2c_pad_s_func = { + .bus_new_4 = gf119_i2c_bus_new, + .aux_new_6 = gm200_i2c_aux_new, + .mode = gm200_i2c_pad_mode, +}; + +int +gm200_i2c_pad_s_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&gm200_i2c_pad_s_func, i2c, id, ppad); +} + +static const struct nvkm_i2c_pad_func +gm200_i2c_pad_x_func = { + .bus_new_4 = gf119_i2c_bus_new, + .aux_new_6 = gm200_i2c_aux_new, +}; + +int +gm200_i2c_pad_x_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&gm200_i2c_pad_x_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c new file mode 100644 index 000000000..310046ad9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c @@ -0,0 +1,36 @@ +/* + * Copyright 2014 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 "pad.h" +#include "bus.h" + +static const struct nvkm_i2c_pad_func +nv04_i2c_pad_func = { + .bus_new_0 = nv04_i2c_bus_new, +}; + +int +nv04_i2c_pad_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&nv04_i2c_pad_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c new file mode 100644 index 000000000..dda6fc0b0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c @@ -0,0 +1,36 @@ +/* + * Copyright 2014 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 "pad.h" +#include "bus.h" + +static const struct nvkm_i2c_pad_func +nv4e_i2c_pad_func = { + .bus_new_4 = nv4e_i2c_bus_new, +}; + +int +nv4e_i2c_pad_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&nv4e_i2c_pad_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c new file mode 100644 index 000000000..a03f25b19 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c @@ -0,0 +1,36 @@ +/* + * Copyright 2014 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 "pad.h" +#include "bus.h" + +static const struct nvkm_i2c_pad_func +nv50_i2c_pad_func = { + .bus_new_4 = nv50_i2c_bus_new, +}; + +int +nv50_i2c_pad_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ + return nvkm_i2c_pad_new_(&nv50_i2c_pad_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h new file mode 100644 index 000000000..f9d79f72f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_I2C_PRIV_H__ +#define __NVKM_I2C_PRIV_H__ +#define nvkm_i2c(p) container_of((p), struct nvkm_i2c, subdev) +#include + +int nvkm_i2c_new_(const struct nvkm_i2c_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_i2c **); + +struct nvkm_i2c_func { + int (*pad_x_new)(struct nvkm_i2c *, int id, struct nvkm_i2c_pad **); + int (*pad_s_new)(struct nvkm_i2c *, int id, struct nvkm_i2c_pad **); + + /* number of native dp aux channels present */ + int aux; + + /* read and ack pending interrupts, returning only data + * for ports that have not been masked off, while still + * performing the ack for anything that was pending. + */ + void (*aux_stat)(struct nvkm_i2c *, u32 *, u32 *, u32 *, u32 *); + + /* mask on/off interrupt types for a given set of auxch + */ + void (*aux_mask)(struct nvkm_i2c *, u32, u32, u32); + + /* enable/disable HW-initiated DPCD reads + */ + void (*aux_autodpcd)(struct nvkm_i2c *, int aux, bool enable); +}; + +void g94_aux_stat(struct nvkm_i2c *, u32 *, u32 *, u32 *, u32 *); +void g94_aux_mask(struct nvkm_i2c *, u32, u32, u32); + +void gk104_aux_stat(struct nvkm_i2c *, u32 *, u32 *, u32 *, u32 *); +void gk104_aux_mask(struct nvkm_i2c *, u32, u32, u32); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild new file mode 100644 index 000000000..6634bcdc5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/iccsense/base.o +nvkm-y += nvkm/subdev/iccsense/gf100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c new file mode 100644 index 000000000..8f0ccd366 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c @@ -0,0 +1,331 @@ +/* + * Copyright 2015 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +#include +#include +#include +#include +#include + +static bool +nvkm_iccsense_validate_device(struct i2c_adapter *i2c, u8 addr, + enum nvbios_extdev_type type) +{ + switch (type) { + case NVBIOS_EXTDEV_INA209: + case NVBIOS_EXTDEV_INA219: + return nv_rd16i2cr(i2c, addr, 0x0) >= 0; + case NVBIOS_EXTDEV_INA3221: + return nv_rd16i2cr(i2c, addr, 0xff) == 0x3220 && + nv_rd16i2cr(i2c, addr, 0xfe) == 0x5449; + default: + return false; + } +} + +static int +nvkm_iccsense_poll_lane(struct i2c_adapter *i2c, u8 addr, u8 shunt_reg, + u8 shunt_shift, u8 bus_reg, u8 bus_shift, u8 shunt, + u16 lsb) +{ + int vshunt = nv_rd16i2cr(i2c, addr, shunt_reg); + int vbus = nv_rd16i2cr(i2c, addr, bus_reg); + + if (vshunt < 0 || vbus < 0) + return -EINVAL; + + vshunt >>= shunt_shift; + vbus >>= bus_shift; + + return vbus * vshunt * lsb / shunt; +} + +static int +nvkm_iccsense_ina2x9_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail, + u8 shunt_reg, u8 bus_reg) +{ + return nvkm_iccsense_poll_lane(rail->sensor->i2c, rail->sensor->addr, + shunt_reg, 0, bus_reg, 3, rail->mohm, + 10 * 4); +} + +static int +nvkm_iccsense_ina209_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail) +{ + return nvkm_iccsense_ina2x9_read(iccsense, rail, 3, 4); +} + +static int +nvkm_iccsense_ina219_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail) +{ + return nvkm_iccsense_ina2x9_read(iccsense, rail, 1, 2); +} + +static int +nvkm_iccsense_ina3221_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail) +{ + return nvkm_iccsense_poll_lane(rail->sensor->i2c, rail->sensor->addr, + 1 + (rail->idx * 2), 3, + 2 + (rail->idx * 2), 3, rail->mohm, + 40 * 8); +} + +static void +nvkm_iccsense_sensor_config(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_sensor *sensor) +{ + struct nvkm_subdev *subdev = &iccsense->subdev; + nvkm_trace(subdev, "write config of extdev %i: 0x%04x\n", sensor->id, sensor->config); + nv_wr16i2cr(sensor->i2c, sensor->addr, 0x00, sensor->config); +} + +int +nvkm_iccsense_read_all(struct nvkm_iccsense *iccsense) +{ + int result = 0; + struct nvkm_iccsense_rail *rail; + + if (!iccsense) + return -EINVAL; + + list_for_each_entry(rail, &iccsense->rails, head) { + int res; + if (!rail->read) + return -ENODEV; + + res = rail->read(iccsense, rail); + if (res < 0) + return res; + result += res; + } + return result; +} + +static void * +nvkm_iccsense_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); + struct nvkm_iccsense_sensor *sensor, *tmps; + struct nvkm_iccsense_rail *rail, *tmpr; + + list_for_each_entry_safe(sensor, tmps, &iccsense->sensors, head) { + list_del(&sensor->head); + kfree(sensor); + } + list_for_each_entry_safe(rail, tmpr, &iccsense->rails, head) { + list_del(&rail->head); + kfree(rail); + } + + return iccsense; +} + +static struct nvkm_iccsense_sensor* +nvkm_iccsense_create_sensor(struct nvkm_iccsense *iccsense, u8 id) +{ + struct nvkm_subdev *subdev = &iccsense->subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvkm_i2c *i2c = subdev->device->i2c; + struct nvbios_extdev_func extdev; + struct nvkm_i2c_bus *i2c_bus; + struct nvkm_iccsense_sensor *sensor; + u8 addr; + + if (!i2c || !bios || nvbios_extdev_parse(bios, id, &extdev)) + return NULL; + + if (extdev.type == 0xff) + return NULL; + + if (extdev.type != NVBIOS_EXTDEV_INA209 && + extdev.type != NVBIOS_EXTDEV_INA219 && + extdev.type != NVBIOS_EXTDEV_INA3221) { + iccsense->data_valid = false; + nvkm_error(subdev, "Unknown sensor type %x, power reading " + "disabled\n", extdev.type); + return NULL; + } + + if (extdev.bus) + i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_SEC); + else + i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_PRI); + if (!i2c_bus) + return NULL; + + addr = extdev.addr >> 1; + if (!nvkm_iccsense_validate_device(&i2c_bus->i2c, addr, + extdev.type)) { + iccsense->data_valid = false; + nvkm_warn(subdev, "found invalid sensor id: %i, power reading" + "might be invalid\n", id); + return NULL; + } + + sensor = kmalloc(sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return NULL; + + list_add_tail(&sensor->head, &iccsense->sensors); + sensor->id = id; + sensor->type = extdev.type; + sensor->i2c = &i2c_bus->i2c; + sensor->addr = addr; + sensor->config = 0x0; + return sensor; +} + +static struct nvkm_iccsense_sensor* +nvkm_iccsense_get_sensor(struct nvkm_iccsense *iccsense, u8 id) +{ + struct nvkm_iccsense_sensor *sensor; + list_for_each_entry(sensor, &iccsense->sensors, head) { + if (sensor->id == id) + return sensor; + } + return nvkm_iccsense_create_sensor(iccsense, id); +} + +static int +nvkm_iccsense_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); + struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_power_budget budget; + struct nvbios_iccsense stbl; + int i, ret; + + if (!bios) + return 0; + + ret = nvbios_power_budget_header(bios, &budget); + if (!ret && budget.cap_entry != 0xff) { + struct nvbios_power_budget_entry entry; + ret = nvbios_power_budget_entry(bios, &budget, + budget.cap_entry, &entry); + if (!ret) { + iccsense->power_w_max = entry.avg_w; + iccsense->power_w_crit = entry.max_w; + } + } + + if (nvbios_iccsense_parse(bios, &stbl) || !stbl.nr_entry) + return 0; + + iccsense->data_valid = true; + for (i = 0; i < stbl.nr_entry; ++i) { + struct pwr_rail_t *pwr_rail = &stbl.rail[i]; + struct nvkm_iccsense_sensor *sensor; + int r; + + if (pwr_rail->mode != 1 || !pwr_rail->resistor_count) + continue; + + sensor = nvkm_iccsense_get_sensor(iccsense, pwr_rail->extdev_id); + if (!sensor) + continue; + + if (!sensor->config) + sensor->config = pwr_rail->config; + else if (sensor->config != pwr_rail->config) + nvkm_error(subdev, "config mismatch found for extdev %i\n", pwr_rail->extdev_id); + + for (r = 0; r < pwr_rail->resistor_count; ++r) { + struct nvkm_iccsense_rail *rail; + struct pwr_rail_resistor_t *res = &pwr_rail->resistors[r]; + int (*read)(struct nvkm_iccsense *, + struct nvkm_iccsense_rail *); + + if (!res->mohm || !res->enabled) + continue; + + switch (sensor->type) { + case NVBIOS_EXTDEV_INA209: + read = nvkm_iccsense_ina209_read; + break; + case NVBIOS_EXTDEV_INA219: + read = nvkm_iccsense_ina219_read; + break; + case NVBIOS_EXTDEV_INA3221: + read = nvkm_iccsense_ina3221_read; + break; + default: + continue; + } + + rail = kmalloc(sizeof(*rail), GFP_KERNEL); + if (!rail) + return -ENOMEM; + + rail->read = read; + rail->sensor = sensor; + rail->idx = r; + rail->mohm = res->mohm; + nvkm_debug(subdev, "create rail for extdev %i: { idx: %i, mohm: %i }\n", pwr_rail->extdev_id, r, rail->mohm); + list_add_tail(&rail->head, &iccsense->rails); + } + } + return 0; +} + +static int +nvkm_iccsense_init(struct nvkm_subdev *subdev) +{ + struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); + struct nvkm_iccsense_sensor *sensor; + list_for_each_entry(sensor, &iccsense->sensors, head) + nvkm_iccsense_sensor_config(iccsense, sensor); + return 0; +} + +static const struct nvkm_subdev_func +iccsense_func = { + .oneinit = nvkm_iccsense_oneinit, + .init = nvkm_iccsense_init, + .dtor = nvkm_iccsense_dtor, +}; + +void +nvkm_iccsense_ctor(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_iccsense *iccsense) +{ + nvkm_subdev_ctor(&iccsense_func, device, type, inst, &iccsense->subdev); +} + +int +nvkm_iccsense_new_(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_iccsense **iccsense) +{ + if (!(*iccsense = kzalloc(sizeof(**iccsense), GFP_KERNEL))) + return -ENOMEM; + INIT_LIST_HEAD(&(*iccsense)->sensors); + INIT_LIST_HEAD(&(*iccsense)->rails); + nvkm_iccsense_ctor(device, type, inst, *iccsense); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c new file mode 100644 index 000000000..3eabf4944 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Karol Herbst + * + * 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: Karol Herbst + */ +#include "priv.h" + +int +gf100_iccsense_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_iccsense **piccsense) +{ + return nvkm_iccsense_new_(device, type, inst, piccsense); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h new file mode 100644 index 000000000..c33441124 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_ICCSENSE_PRIV_H__ +#define __NVKM_ICCSENSE_PRIV_H__ +#define nvkm_iccsense(p) container_of((p), struct nvkm_iccsense, subdev) +#include +#include + +struct nvkm_iccsense_sensor { + struct list_head head; + int id; + enum nvbios_extdev_type type; + struct i2c_adapter *i2c; + u8 addr; + u16 config; +}; + +struct nvkm_iccsense_rail { + struct list_head head; + int (*read)(struct nvkm_iccsense *, struct nvkm_iccsense_rail *); + struct nvkm_iccsense_sensor *sensor; + u8 idx; + u8 mohm; +}; + +void nvkm_iccsense_ctor(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_iccsense *); +int nvkm_iccsense_new_(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_iccsense **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild new file mode 100644 index 000000000..06cbe19ce --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/instmem/base.o +nvkm-y += nvkm/subdev/instmem/nv04.o +nvkm-y += nvkm/subdev/instmem/nv40.o +nvkm-y += nvkm/subdev/instmem/nv50.o +nvkm-y += nvkm/subdev/instmem/gk20a.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c new file mode 100644 index 000000000..cd8163a52 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c @@ -0,0 +1,246 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +/****************************************************************************** + * instmem object base implementation + *****************************************************************************/ +static void +nvkm_instobj_load(struct nvkm_instobj *iobj) +{ + struct nvkm_memory *memory = &iobj->memory; + const u64 size = nvkm_memory_size(memory); + void __iomem *map; + int i; + + if (!(map = nvkm_kmap(memory))) { + for (i = 0; i < size; i += 4) + nvkm_wo32(memory, i, iobj->suspend[i / 4]); + } else { + memcpy_toio(map, iobj->suspend, size); + } + nvkm_done(memory); + + kvfree(iobj->suspend); + iobj->suspend = NULL; +} + +static int +nvkm_instobj_save(struct nvkm_instobj *iobj) +{ + struct nvkm_memory *memory = &iobj->memory; + const u64 size = nvkm_memory_size(memory); + void __iomem *map; + int i; + + iobj->suspend = kvmalloc(size, GFP_KERNEL); + if (!iobj->suspend) + return -ENOMEM; + + if (!(map = nvkm_kmap(memory))) { + for (i = 0; i < size; i += 4) + iobj->suspend[i / 4] = nvkm_ro32(memory, i); + } else { + memcpy_fromio(iobj->suspend, map, size); + } + nvkm_done(memory); + return 0; +} + +void +nvkm_instobj_dtor(struct nvkm_instmem *imem, struct nvkm_instobj *iobj) +{ + spin_lock(&imem->lock); + list_del(&iobj->head); + spin_unlock(&imem->lock); +} + +void +nvkm_instobj_ctor(const struct nvkm_memory_func *func, + struct nvkm_instmem *imem, struct nvkm_instobj *iobj) +{ + nvkm_memory_ctor(func, &iobj->memory); + iobj->suspend = NULL; + spin_lock(&imem->lock); + list_add_tail(&iobj->head, &imem->list); + spin_unlock(&imem->lock); +} + +int +nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, + struct nvkm_memory **pmemory) +{ + struct nvkm_subdev *subdev = &imem->subdev; + struct nvkm_memory *memory = NULL; + u32 offset; + int ret; + + ret = imem->func->memory_new(imem, size, align, zero, &memory); + if (ret) { + nvkm_error(subdev, "OOM: %08x %08x %d\n", size, align, ret); + goto done; + } + + nvkm_trace(subdev, "new %08x %08x %d: %010llx %010llx\n", size, align, + zero, nvkm_memory_addr(memory), nvkm_memory_size(memory)); + + if (!imem->func->zero && zero) { + void __iomem *map = nvkm_kmap(memory); + if (unlikely(!map)) { + for (offset = 0; offset < size; offset += 4) + nvkm_wo32(memory, offset, 0x00000000); + } else { + memset_io(map, 0x00, size); + } + nvkm_done(memory); + } + +done: + if (ret) + nvkm_memory_unref(&memory); + *pmemory = memory; + return ret; +} + +/****************************************************************************** + * instmem subdev base implementation + *****************************************************************************/ + +u32 +nvkm_instmem_rd32(struct nvkm_instmem *imem, u32 addr) +{ + return imem->func->rd32(imem, addr); +} + +void +nvkm_instmem_wr32(struct nvkm_instmem *imem, u32 addr, u32 data) +{ + return imem->func->wr32(imem, addr, data); +} + +void +nvkm_instmem_boot(struct nvkm_instmem *imem) +{ + /* Separate bootstrapped objects from normal list, as we need + * to make sure they're accessed with the slowpath on suspend + * and resume. + */ + struct nvkm_instobj *iobj, *itmp; + spin_lock(&imem->lock); + list_for_each_entry_safe(iobj, itmp, &imem->list, head) { + list_move_tail(&iobj->head, &imem->boot); + } + spin_unlock(&imem->lock); +} + +static int +nvkm_instmem_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_instmem *imem = nvkm_instmem(subdev); + struct nvkm_instobj *iobj; + + if (suspend) { + list_for_each_entry(iobj, &imem->list, head) { + int ret = nvkm_instobj_save(iobj); + if (ret) + return ret; + } + + nvkm_bar_bar2_fini(subdev->device); + + list_for_each_entry(iobj, &imem->boot, head) { + int ret = nvkm_instobj_save(iobj); + if (ret) + return ret; + } + } + + if (imem->func->fini) + imem->func->fini(imem); + + return 0; +} + +static int +nvkm_instmem_init(struct nvkm_subdev *subdev) +{ + struct nvkm_instmem *imem = nvkm_instmem(subdev); + struct nvkm_instobj *iobj; + + list_for_each_entry(iobj, &imem->boot, head) { + if (iobj->suspend) + nvkm_instobj_load(iobj); + } + + nvkm_bar_bar2_init(subdev->device); + + list_for_each_entry(iobj, &imem->list, head) { + if (iobj->suspend) + nvkm_instobj_load(iobj); + } + + return 0; +} + +static int +nvkm_instmem_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_instmem *imem = nvkm_instmem(subdev); + if (imem->func->oneinit) + return imem->func->oneinit(imem); + return 0; +} + +static void * +nvkm_instmem_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_instmem *imem = nvkm_instmem(subdev); + void *data = imem; + if (imem->func->dtor) + data = imem->func->dtor(imem); + mutex_destroy(&imem->mutex); + return data; +} + +static const struct nvkm_subdev_func +nvkm_instmem = { + .dtor = nvkm_instmem_dtor, + .oneinit = nvkm_instmem_oneinit, + .init = nvkm_instmem_init, + .fini = nvkm_instmem_fini, +}; + +void +nvkm_instmem_ctor(const struct nvkm_instmem_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_instmem *imem) +{ + nvkm_subdev_ctor(&nvkm_instmem, device, type, inst, &imem->subdev); + imem->func = func; + spin_lock_init(&imem->lock); + INIT_LIST_HEAD(&imem->list); + INIT_LIST_HEAD(&imem->boot); + mutex_init(&imem->mutex); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c new file mode 100644 index 000000000..648ecf5a8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * GK20A does not have dedicated video memory, and to accurately represent this + * fact Nouveau will not create a RAM device for it. Therefore its instmem + * implementation must be done directly on top of system memory, while + * preserving coherency for read and write operations. + * + * Instmem can be allocated through two means: + * 1) If an IOMMU unit has been probed, the IOMMU API is used to make memory + * pages contiguous to the GPU. This is the preferred way. + * 2) If no IOMMU unit is probed, the DMA API is used to allocate physically + * contiguous memory. + * + * In both cases CPU read and writes are performed by creating a write-combined + * mapping. The GPU L2 cache must thus be flushed/invalidated when required. To + * be conservative we do this every time we acquire or release an instobj, but + * ideally L2 management should be handled at a higher level. + * + * To improve performance, CPU mappings are not removed upon instobj release. + * Instead they are placed into a LRU list to be recycled when the mapped space + * goes beyond a certain threshold. At the moment this limit is 1MB. + */ +#include "priv.h" + +#include +#include +#include +#include + +struct gk20a_instobj { + struct nvkm_memory memory; + struct nvkm_mm_node *mn; + struct gk20a_instmem *imem; + + /* CPU mapping */ + u32 *vaddr; +}; +#define gk20a_instobj(p) container_of((p), struct gk20a_instobj, memory) + +/* + * Used for objects allocated using the DMA API + */ +struct gk20a_instobj_dma { + struct gk20a_instobj base; + + dma_addr_t handle; + struct nvkm_mm_node r; +}; +#define gk20a_instobj_dma(p) \ + container_of(gk20a_instobj(p), struct gk20a_instobj_dma, base) + +/* + * Used for objects flattened using the IOMMU API + */ +struct gk20a_instobj_iommu { + struct gk20a_instobj base; + + /* to link into gk20a_instmem::vaddr_lru */ + struct list_head vaddr_node; + /* how many clients are using vaddr? */ + u32 use_cpt; + + /* will point to the higher half of pages */ + dma_addr_t *dma_addrs; + /* array of base.mem->size pages (+ dma_addr_ts) */ + struct page *pages[]; +}; +#define gk20a_instobj_iommu(p) \ + container_of(gk20a_instobj(p), struct gk20a_instobj_iommu, base) + +struct gk20a_instmem { + struct nvkm_instmem base; + + /* protects vaddr_* and gk20a_instobj::vaddr* */ + struct mutex lock; + + /* CPU mappings LRU */ + unsigned int vaddr_use; + unsigned int vaddr_max; + struct list_head vaddr_lru; + + /* Only used if IOMMU if present */ + struct mutex *mm_mutex; + struct nvkm_mm *mm; + struct iommu_domain *domain; + unsigned long iommu_pgshift; + u16 iommu_bit; + + /* Only used by DMA API */ + unsigned long attrs; +}; +#define gk20a_instmem(p) container_of((p), struct gk20a_instmem, base) + +static enum nvkm_memory_target +gk20a_instobj_target(struct nvkm_memory *memory) +{ + return NVKM_MEM_TARGET_NCOH; +} + +static u8 +gk20a_instobj_page(struct nvkm_memory *memory) +{ + return 12; +} + +static u64 +gk20a_instobj_addr(struct nvkm_memory *memory) +{ + return (u64)gk20a_instobj(memory)->mn->offset << 12; +} + +static u64 +gk20a_instobj_size(struct nvkm_memory *memory) +{ + return (u64)gk20a_instobj(memory)->mn->length << 12; +} + +/* + * Recycle the vaddr of obj. Must be called with gk20a_instmem::lock held. + */ +static void +gk20a_instobj_iommu_recycle_vaddr(struct gk20a_instobj_iommu *obj) +{ + struct gk20a_instmem *imem = obj->base.imem; + /* there should not be any user left... */ + WARN_ON(obj->use_cpt); + list_del(&obj->vaddr_node); + vunmap(obj->base.vaddr); + obj->base.vaddr = NULL; + imem->vaddr_use -= nvkm_memory_size(&obj->base.memory); + nvkm_debug(&imem->base.subdev, "vaddr used: %x/%x\n", imem->vaddr_use, + imem->vaddr_max); +} + +/* + * Must be called while holding gk20a_instmem::lock + */ +static void +gk20a_instmem_vaddr_gc(struct gk20a_instmem *imem, const u64 size) +{ + while (imem->vaddr_use + size > imem->vaddr_max) { + /* no candidate that can be unmapped, abort... */ + if (list_empty(&imem->vaddr_lru)) + break; + + gk20a_instobj_iommu_recycle_vaddr( + list_first_entry(&imem->vaddr_lru, + struct gk20a_instobj_iommu, vaddr_node)); + } +} + +static void __iomem * +gk20a_instobj_acquire_dma(struct nvkm_memory *memory) +{ + struct gk20a_instobj *node = gk20a_instobj(memory); + struct gk20a_instmem *imem = node->imem; + struct nvkm_ltc *ltc = imem->base.subdev.device->ltc; + + nvkm_ltc_flush(ltc); + + return node->vaddr; +} + +static void __iomem * +gk20a_instobj_acquire_iommu(struct nvkm_memory *memory) +{ + struct gk20a_instobj_iommu *node = gk20a_instobj_iommu(memory); + struct gk20a_instmem *imem = node->base.imem; + struct nvkm_ltc *ltc = imem->base.subdev.device->ltc; + const u64 size = nvkm_memory_size(memory); + + nvkm_ltc_flush(ltc); + + mutex_lock(&imem->lock); + + if (node->base.vaddr) { + if (!node->use_cpt) { + /* remove from LRU list since mapping in use again */ + list_del(&node->vaddr_node); + } + goto out; + } + + /* try to free some address space if we reached the limit */ + gk20a_instmem_vaddr_gc(imem, size); + + /* map the pages */ + node->base.vaddr = vmap(node->pages, size >> PAGE_SHIFT, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); + if (!node->base.vaddr) { + nvkm_error(&imem->base.subdev, "cannot map instobj - " + "this is not going to end well...\n"); + goto out; + } + + imem->vaddr_use += size; + nvkm_debug(&imem->base.subdev, "vaddr used: %x/%x\n", + imem->vaddr_use, imem->vaddr_max); + +out: + node->use_cpt++; + mutex_unlock(&imem->lock); + + return node->base.vaddr; +} + +static void +gk20a_instobj_release_dma(struct nvkm_memory *memory) +{ + struct gk20a_instobj *node = gk20a_instobj(memory); + struct gk20a_instmem *imem = node->imem; + struct nvkm_ltc *ltc = imem->base.subdev.device->ltc; + + /* in case we got a write-combined mapping */ + wmb(); + nvkm_ltc_invalidate(ltc); +} + +static void +gk20a_instobj_release_iommu(struct nvkm_memory *memory) +{ + struct gk20a_instobj_iommu *node = gk20a_instobj_iommu(memory); + struct gk20a_instmem *imem = node->base.imem; + struct nvkm_ltc *ltc = imem->base.subdev.device->ltc; + + mutex_lock(&imem->lock); + + /* we should at least have one user to release... */ + if (WARN_ON(node->use_cpt == 0)) + goto out; + + /* add unused objs to the LRU list to recycle their mapping */ + if (--node->use_cpt == 0) + list_add_tail(&node->vaddr_node, &imem->vaddr_lru); + +out: + mutex_unlock(&imem->lock); + + wmb(); + nvkm_ltc_invalidate(ltc); +} + +static u32 +gk20a_instobj_rd32(struct nvkm_memory *memory, u64 offset) +{ + struct gk20a_instobj *node = gk20a_instobj(memory); + + return node->vaddr[offset / 4]; +} + +static void +gk20a_instobj_wr32(struct nvkm_memory *memory, u64 offset, u32 data) +{ + struct gk20a_instobj *node = gk20a_instobj(memory); + + node->vaddr[offset / 4] = data; +} + +static int +gk20a_instobj_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm, + struct nvkm_vma *vma, void *argv, u32 argc) +{ + struct gk20a_instobj *node = gk20a_instobj(memory); + struct nvkm_vmm_map map = { + .memory = &node->memory, + .offset = offset, + .mem = node->mn, + }; + + return nvkm_vmm_map(vmm, vma, argv, argc, &map); +} + +static void * +gk20a_instobj_dtor_dma(struct nvkm_memory *memory) +{ + struct gk20a_instobj_dma *node = gk20a_instobj_dma(memory); + struct gk20a_instmem *imem = node->base.imem; + struct device *dev = imem->base.subdev.device->dev; + + if (unlikely(!node->base.vaddr)) + goto out; + + dma_free_attrs(dev, (u64)node->base.mn->length << PAGE_SHIFT, + node->base.vaddr, node->handle, imem->attrs); + +out: + return node; +} + +static void * +gk20a_instobj_dtor_iommu(struct nvkm_memory *memory) +{ + struct gk20a_instobj_iommu *node = gk20a_instobj_iommu(memory); + struct gk20a_instmem *imem = node->base.imem; + struct device *dev = imem->base.subdev.device->dev; + struct nvkm_mm_node *r = node->base.mn; + int i; + + if (unlikely(!r)) + goto out; + + mutex_lock(&imem->lock); + + /* vaddr has already been recycled */ + if (node->base.vaddr) + gk20a_instobj_iommu_recycle_vaddr(node); + + mutex_unlock(&imem->lock); + + /* clear IOMMU bit to unmap pages */ + r->offset &= ~BIT(imem->iommu_bit - imem->iommu_pgshift); + + /* Unmap pages from GPU address space and free them */ + for (i = 0; i < node->base.mn->length; i++) { + iommu_unmap(imem->domain, + (r->offset + i) << imem->iommu_pgshift, PAGE_SIZE); + dma_unmap_page(dev, node->dma_addrs[i], PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(node->pages[i]); + } + + /* Release area from GPU address space */ + mutex_lock(imem->mm_mutex); + nvkm_mm_free(imem->mm, &r); + mutex_unlock(imem->mm_mutex); + +out: + return node; +} + +static const struct nvkm_memory_func +gk20a_instobj_func_dma = { + .dtor = gk20a_instobj_dtor_dma, + .target = gk20a_instobj_target, + .page = gk20a_instobj_page, + .addr = gk20a_instobj_addr, + .size = gk20a_instobj_size, + .acquire = gk20a_instobj_acquire_dma, + .release = gk20a_instobj_release_dma, + .map = gk20a_instobj_map, +}; + +static const struct nvkm_memory_func +gk20a_instobj_func_iommu = { + .dtor = gk20a_instobj_dtor_iommu, + .target = gk20a_instobj_target, + .page = gk20a_instobj_page, + .addr = gk20a_instobj_addr, + .size = gk20a_instobj_size, + .acquire = gk20a_instobj_acquire_iommu, + .release = gk20a_instobj_release_iommu, + .map = gk20a_instobj_map, +}; + +static const struct nvkm_memory_ptrs +gk20a_instobj_ptrs = { + .rd32 = gk20a_instobj_rd32, + .wr32 = gk20a_instobj_wr32, +}; + +static int +gk20a_instobj_ctor_dma(struct gk20a_instmem *imem, u32 npages, u32 align, + struct gk20a_instobj **_node) +{ + struct gk20a_instobj_dma *node; + struct nvkm_subdev *subdev = &imem->base.subdev; + struct device *dev = subdev->device->dev; + + if (!(node = kzalloc(sizeof(*node), GFP_KERNEL))) + return -ENOMEM; + *_node = &node->base; + + nvkm_memory_ctor(&gk20a_instobj_func_dma, &node->base.memory); + node->base.memory.ptrs = &gk20a_instobj_ptrs; + + node->base.vaddr = dma_alloc_attrs(dev, npages << PAGE_SHIFT, + &node->handle, GFP_KERNEL, + imem->attrs); + if (!node->base.vaddr) { + nvkm_error(subdev, "cannot allocate DMA memory\n"); + return -ENOMEM; + } + + /* alignment check */ + if (unlikely(node->handle & (align - 1))) + nvkm_warn(subdev, + "memory not aligned as requested: %pad (0x%x)\n", + &node->handle, align); + + /* present memory for being mapped using small pages */ + node->r.type = 12; + node->r.offset = node->handle >> 12; + node->r.length = (npages << PAGE_SHIFT) >> 12; + + node->base.mn = &node->r; + return 0; +} + +static int +gk20a_instobj_ctor_iommu(struct gk20a_instmem *imem, u32 npages, u32 align, + struct gk20a_instobj **_node) +{ + struct gk20a_instobj_iommu *node; + struct nvkm_subdev *subdev = &imem->base.subdev; + struct device *dev = subdev->device->dev; + struct nvkm_mm_node *r; + int ret; + int i; + + /* + * despite their variable size, instmem allocations are small enough + * (< 1 page) to be handled by kzalloc + */ + if (!(node = kzalloc(sizeof(*node) + ((sizeof(node->pages[0]) + + sizeof(*node->dma_addrs)) * npages), GFP_KERNEL))) + return -ENOMEM; + *_node = &node->base; + node->dma_addrs = (void *)(node->pages + npages); + + nvkm_memory_ctor(&gk20a_instobj_func_iommu, &node->base.memory); + node->base.memory.ptrs = &gk20a_instobj_ptrs; + + /* Allocate backing memory */ + for (i = 0; i < npages; i++) { + struct page *p = alloc_page(GFP_KERNEL); + dma_addr_t dma_adr; + + if (p == NULL) { + ret = -ENOMEM; + goto free_pages; + } + node->pages[i] = p; + dma_adr = dma_map_page(dev, p, 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, dma_adr)) { + nvkm_error(subdev, "DMA mapping error!\n"); + ret = -ENOMEM; + goto free_pages; + } + node->dma_addrs[i] = dma_adr; + } + + mutex_lock(imem->mm_mutex); + /* Reserve area from GPU address space */ + ret = nvkm_mm_head(imem->mm, 0, 1, npages, npages, + align >> imem->iommu_pgshift, &r); + mutex_unlock(imem->mm_mutex); + if (ret) { + nvkm_error(subdev, "IOMMU space is full!\n"); + goto free_pages; + } + + /* Map into GPU address space */ + for (i = 0; i < npages; i++) { + u32 offset = (r->offset + i) << imem->iommu_pgshift; + + ret = iommu_map(imem->domain, offset, node->dma_addrs[i], + PAGE_SIZE, IOMMU_READ | IOMMU_WRITE); + if (ret < 0) { + nvkm_error(subdev, "IOMMU mapping failure: %d\n", ret); + + while (i-- > 0) { + offset -= PAGE_SIZE; + iommu_unmap(imem->domain, offset, PAGE_SIZE); + } + goto release_area; + } + } + + /* IOMMU bit tells that an address is to be resolved through the IOMMU */ + r->offset |= BIT(imem->iommu_bit - imem->iommu_pgshift); + + node->base.mn = r; + return 0; + +release_area: + mutex_lock(imem->mm_mutex); + nvkm_mm_free(imem->mm, &r); + mutex_unlock(imem->mm_mutex); + +free_pages: + for (i = 0; i < npages && node->pages[i] != NULL; i++) { + dma_addr_t dma_addr = node->dma_addrs[i]; + if (dma_addr) + dma_unmap_page(dev, dma_addr, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(node->pages[i]); + } + + return ret; +} + +static int +gk20a_instobj_new(struct nvkm_instmem *base, u32 size, u32 align, bool zero, + struct nvkm_memory **pmemory) +{ + struct gk20a_instmem *imem = gk20a_instmem(base); + struct nvkm_subdev *subdev = &imem->base.subdev; + struct gk20a_instobj *node = NULL; + int ret; + + nvkm_debug(subdev, "%s (%s): size: %x align: %x\n", __func__, + imem->domain ? "IOMMU" : "DMA", size, align); + + /* Round size and align to page bounds */ + size = max(roundup(size, PAGE_SIZE), PAGE_SIZE); + align = max(roundup(align, PAGE_SIZE), PAGE_SIZE); + + if (imem->domain) + ret = gk20a_instobj_ctor_iommu(imem, size >> PAGE_SHIFT, + align, &node); + else + ret = gk20a_instobj_ctor_dma(imem, size >> PAGE_SHIFT, + align, &node); + *pmemory = node ? &node->memory : NULL; + if (ret) + return ret; + + node->imem = imem; + + nvkm_debug(subdev, "alloc size: 0x%x, align: 0x%x, gaddr: 0x%llx\n", + size, align, (u64)node->mn->offset << 12); + + return 0; +} + +static void * +gk20a_instmem_dtor(struct nvkm_instmem *base) +{ + struct gk20a_instmem *imem = gk20a_instmem(base); + + /* perform some sanity checks... */ + if (!list_empty(&imem->vaddr_lru)) + nvkm_warn(&base->subdev, "instobj LRU not empty!\n"); + + if (imem->vaddr_use != 0) + nvkm_warn(&base->subdev, "instobj vmap area not empty! " + "0x%x bytes still mapped\n", imem->vaddr_use); + + return imem; +} + +static const struct nvkm_instmem_func +gk20a_instmem = { + .dtor = gk20a_instmem_dtor, + .memory_new = gk20a_instobj_new, + .zero = false, +}; + +int +gk20a_instmem_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_instmem **pimem) +{ + struct nvkm_device_tegra *tdev = device->func->tegra(device); + struct gk20a_instmem *imem; + + if (!(imem = kzalloc(sizeof(*imem), GFP_KERNEL))) + return -ENOMEM; + nvkm_instmem_ctor(&gk20a_instmem, device, type, inst, &imem->base); + mutex_init(&imem->lock); + *pimem = &imem->base; + + /* do not allow more than 1MB of CPU-mapped instmem */ + imem->vaddr_use = 0; + imem->vaddr_max = 0x100000; + INIT_LIST_HEAD(&imem->vaddr_lru); + + if (tdev->iommu.domain) { + imem->mm_mutex = &tdev->iommu.mutex; + imem->mm = &tdev->iommu.mm; + imem->domain = tdev->iommu.domain; + imem->iommu_pgshift = tdev->iommu.pgshift; + imem->iommu_bit = tdev->func->iommu_bit; + + nvkm_info(&imem->base.subdev, "using IOMMU\n"); + } else { + imem->attrs = DMA_ATTR_WEAK_ORDERING | + DMA_ATTR_WRITE_COMBINE; + + nvkm_info(&imem->base.subdev, "using DMA API\n"); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c new file mode 100644 index 000000000..25603b01d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c @@ -0,0 +1,230 @@ +/* + * 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 + */ +#define nv04_instmem(p) container_of((p), struct nv04_instmem, base) +#include "priv.h" + +#include + +struct nv04_instmem { + struct nvkm_instmem base; + struct nvkm_mm heap; +}; + +/****************************************************************************** + * instmem object implementation + *****************************************************************************/ +#define nv04_instobj(p) container_of((p), struct nv04_instobj, base.memory) + +struct nv04_instobj { + struct nvkm_instobj base; + struct nv04_instmem *imem; + struct nvkm_mm_node *node; +}; + +static void +nv04_instobj_wr32(struct nvkm_memory *memory, u64 offset, u32 data) +{ + struct nv04_instobj *iobj = nv04_instobj(memory); + struct nvkm_device *device = iobj->imem->base.subdev.device; + nvkm_wr32(device, 0x700000 + iobj->node->offset + offset, data); +} + +static u32 +nv04_instobj_rd32(struct nvkm_memory *memory, u64 offset) +{ + struct nv04_instobj *iobj = nv04_instobj(memory); + struct nvkm_device *device = iobj->imem->base.subdev.device; + return nvkm_rd32(device, 0x700000 + iobj->node->offset + offset); +} + +static const struct nvkm_memory_ptrs +nv04_instobj_ptrs = { + .rd32 = nv04_instobj_rd32, + .wr32 = nv04_instobj_wr32, +}; + +static void +nv04_instobj_release(struct nvkm_memory *memory) +{ +} + +static void __iomem * +nv04_instobj_acquire(struct nvkm_memory *memory) +{ + struct nv04_instobj *iobj = nv04_instobj(memory); + struct nvkm_device *device = iobj->imem->base.subdev.device; + return device->pri + 0x700000 + iobj->node->offset; +} + +static u64 +nv04_instobj_size(struct nvkm_memory *memory) +{ + return nv04_instobj(memory)->node->length; +} + +static u64 +nv04_instobj_addr(struct nvkm_memory *memory) +{ + return nv04_instobj(memory)->node->offset; +} + +static enum nvkm_memory_target +nv04_instobj_target(struct nvkm_memory *memory) +{ + return NVKM_MEM_TARGET_INST; +} + +static void * +nv04_instobj_dtor(struct nvkm_memory *memory) +{ + struct nv04_instobj *iobj = nv04_instobj(memory); + mutex_lock(&iobj->imem->base.mutex); + nvkm_mm_free(&iobj->imem->heap, &iobj->node); + mutex_unlock(&iobj->imem->base.mutex); + nvkm_instobj_dtor(&iobj->imem->base, &iobj->base); + return iobj; +} + +static const struct nvkm_memory_func +nv04_instobj_func = { + .dtor = nv04_instobj_dtor, + .target = nv04_instobj_target, + .size = nv04_instobj_size, + .addr = nv04_instobj_addr, + .acquire = nv04_instobj_acquire, + .release = nv04_instobj_release, +}; + +static int +nv04_instobj_new(struct nvkm_instmem *base, u32 size, u32 align, bool zero, + struct nvkm_memory **pmemory) +{ + struct nv04_instmem *imem = nv04_instmem(base); + struct nv04_instobj *iobj; + int ret; + + if (!(iobj = kzalloc(sizeof(*iobj), GFP_KERNEL))) + return -ENOMEM; + *pmemory = &iobj->base.memory; + + nvkm_instobj_ctor(&nv04_instobj_func, &imem->base, &iobj->base); + iobj->base.memory.ptrs = &nv04_instobj_ptrs; + iobj->imem = imem; + + mutex_lock(&imem->base.mutex); + ret = nvkm_mm_head(&imem->heap, 0, 1, size, size, align ? align : 1, &iobj->node); + mutex_unlock(&imem->base.mutex); + return ret; +} + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static u32 +nv04_instmem_rd32(struct nvkm_instmem *imem, u32 addr) +{ + return nvkm_rd32(imem->subdev.device, 0x700000 + addr); +} + +static void +nv04_instmem_wr32(struct nvkm_instmem *imem, u32 addr, u32 data) +{ + nvkm_wr32(imem->subdev.device, 0x700000 + addr, data); +} + +static int +nv04_instmem_oneinit(struct nvkm_instmem *base) +{ + struct nv04_instmem *imem = nv04_instmem(base); + struct nvkm_device *device = imem->base.subdev.device; + int ret; + + /* PRAMIN aperture maps over the end of VRAM, reserve it */ + imem->base.reserved = 512 * 1024; + + ret = nvkm_mm_init(&imem->heap, 0, 0, imem->base.reserved, 1); + if (ret) + return ret; + + /* 0x00000-0x10000: reserve for probable vbios image */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x10000, 0, false, + &imem->base.vbios); + if (ret) + return ret; + + /* 0x10000-0x18000: reserve for RAMHT */ + ret = nvkm_ramht_new(device, 0x08000, 0, NULL, &imem->base.ramht); + if (ret) + return ret; + + /* 0x18000-0x18800: reserve for RAMFC (enough for 32 nv30 channels) */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x00800, 0, true, + &imem->base.ramfc); + if (ret) + return ret; + + /* 0x18800-0x18a00: reserve for RAMRO */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x00200, 0, false, + &imem->base.ramro); + if (ret) + return ret; + + return 0; +} + +static void * +nv04_instmem_dtor(struct nvkm_instmem *base) +{ + struct nv04_instmem *imem = nv04_instmem(base); + nvkm_memory_unref(&imem->base.ramfc); + nvkm_memory_unref(&imem->base.ramro); + nvkm_ramht_del(&imem->base.ramht); + nvkm_memory_unref(&imem->base.vbios); + nvkm_mm_fini(&imem->heap); + return imem; +} + +static const struct nvkm_instmem_func +nv04_instmem = { + .dtor = nv04_instmem_dtor, + .oneinit = nv04_instmem_oneinit, + .rd32 = nv04_instmem_rd32, + .wr32 = nv04_instmem_wr32, + .memory_new = nv04_instobj_new, + .zero = false, +}; + +int +nv04_instmem_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_instmem **pimem) +{ + struct nv04_instmem *imem; + + if (!(imem = kzalloc(sizeof(*imem), GFP_KERNEL))) + return -ENOMEM; + nvkm_instmem_ctor(&nv04_instmem, device, type, inst, &imem->base); + *pimem = &imem->base; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c new file mode 100644 index 000000000..6b462f960 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c @@ -0,0 +1,263 @@ +/* + * 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 + */ +#define nv40_instmem(p) container_of((p), struct nv40_instmem, base) +#include "priv.h" + +#include +#include + +struct nv40_instmem { + struct nvkm_instmem base; + struct nvkm_mm heap; + void __iomem *iomem; +}; + +/****************************************************************************** + * instmem object implementation + *****************************************************************************/ +#define nv40_instobj(p) container_of((p), struct nv40_instobj, base.memory) + +struct nv40_instobj { + struct nvkm_instobj base; + struct nv40_instmem *imem; + struct nvkm_mm_node *node; +}; + +static void +nv40_instobj_wr32(struct nvkm_memory *memory, u64 offset, u32 data) +{ + struct nv40_instobj *iobj = nv40_instobj(memory); + iowrite32_native(data, iobj->imem->iomem + iobj->node->offset + offset); +} + +static u32 +nv40_instobj_rd32(struct nvkm_memory *memory, u64 offset) +{ + struct nv40_instobj *iobj = nv40_instobj(memory); + return ioread32_native(iobj->imem->iomem + iobj->node->offset + offset); +} + +static const struct nvkm_memory_ptrs +nv40_instobj_ptrs = { + .rd32 = nv40_instobj_rd32, + .wr32 = nv40_instobj_wr32, +}; + +static void +nv40_instobj_release(struct nvkm_memory *memory) +{ + wmb(); +} + +static void __iomem * +nv40_instobj_acquire(struct nvkm_memory *memory) +{ + struct nv40_instobj *iobj = nv40_instobj(memory); + return iobj->imem->iomem + iobj->node->offset; +} + +static u64 +nv40_instobj_size(struct nvkm_memory *memory) +{ + return nv40_instobj(memory)->node->length; +} + +static u64 +nv40_instobj_addr(struct nvkm_memory *memory) +{ + return nv40_instobj(memory)->node->offset; +} + +static enum nvkm_memory_target +nv40_instobj_target(struct nvkm_memory *memory) +{ + return NVKM_MEM_TARGET_INST; +} + +static void * +nv40_instobj_dtor(struct nvkm_memory *memory) +{ + struct nv40_instobj *iobj = nv40_instobj(memory); + mutex_lock(&iobj->imem->base.mutex); + nvkm_mm_free(&iobj->imem->heap, &iobj->node); + mutex_unlock(&iobj->imem->base.mutex); + nvkm_instobj_dtor(&iobj->imem->base, &iobj->base); + return iobj; +} + +static const struct nvkm_memory_func +nv40_instobj_func = { + .dtor = nv40_instobj_dtor, + .target = nv40_instobj_target, + .size = nv40_instobj_size, + .addr = nv40_instobj_addr, + .acquire = nv40_instobj_acquire, + .release = nv40_instobj_release, +}; + +static int +nv40_instobj_new(struct nvkm_instmem *base, u32 size, u32 align, bool zero, + struct nvkm_memory **pmemory) +{ + struct nv40_instmem *imem = nv40_instmem(base); + struct nv40_instobj *iobj; + int ret; + + if (!(iobj = kzalloc(sizeof(*iobj), GFP_KERNEL))) + return -ENOMEM; + *pmemory = &iobj->base.memory; + + nvkm_instobj_ctor(&nv40_instobj_func, &imem->base, &iobj->base); + iobj->base.memory.ptrs = &nv40_instobj_ptrs; + iobj->imem = imem; + + mutex_lock(&imem->base.mutex); + ret = nvkm_mm_head(&imem->heap, 0, 1, size, size, align ? align : 1, &iobj->node); + mutex_unlock(&imem->base.mutex); + return ret; +} + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static u32 +nv40_instmem_rd32(struct nvkm_instmem *base, u32 addr) +{ + return ioread32_native(nv40_instmem(base)->iomem + addr); +} + +static void +nv40_instmem_wr32(struct nvkm_instmem *base, u32 addr, u32 data) +{ + iowrite32_native(data, nv40_instmem(base)->iomem + addr); +} + +static int +nv40_instmem_oneinit(struct nvkm_instmem *base) +{ + struct nv40_instmem *imem = nv40_instmem(base); + struct nvkm_device *device = imem->base.subdev.device; + int ret, vs; + + /* PRAMIN aperture maps over the end of vram, reserve enough space + * to fit graphics contexts for every channel, the magics come + * from engine/gr/nv40.c + */ + vs = hweight8((nvkm_rd32(device, 0x001540) & 0x0000ff00) >> 8); + if (device->chipset == 0x40) imem->base.reserved = 0x6aa0 * vs; + else if (device->chipset < 0x43) imem->base.reserved = 0x4f00 * vs; + else if (nv44_gr_class(device)) imem->base.reserved = 0x4980 * vs; + else imem->base.reserved = 0x4a40 * vs; + imem->base.reserved += 16 * 1024; + imem->base.reserved *= 32; /* per-channel */ + imem->base.reserved += 512 * 1024; /* pci(e)gart table */ + imem->base.reserved += 512 * 1024; /* object storage */ + imem->base.reserved = round_up(imem->base.reserved, 4096); + + ret = nvkm_mm_init(&imem->heap, 0, 0, imem->base.reserved, 1); + if (ret) + return ret; + + /* 0x00000-0x10000: reserve for probable vbios image */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x10000, 0, false, + &imem->base.vbios); + if (ret) + return ret; + + /* 0x10000-0x18000: reserve for RAMHT */ + ret = nvkm_ramht_new(device, 0x08000, 0, NULL, &imem->base.ramht); + if (ret) + return ret; + + /* 0x18000-0x18200: reserve for RAMRO + * 0x18200-0x20000: padding + */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x08000, 0, false, + &imem->base.ramro); + if (ret) + return ret; + + /* 0x20000-0x21000: reserve for RAMFC + * 0x21000-0x40000: padding and some unknown crap + */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x20000, 0, true, + &imem->base.ramfc); + if (ret) + return ret; + + return 0; +} + +static void * +nv40_instmem_dtor(struct nvkm_instmem *base) +{ + struct nv40_instmem *imem = nv40_instmem(base); + nvkm_memory_unref(&imem->base.ramfc); + nvkm_memory_unref(&imem->base.ramro); + nvkm_ramht_del(&imem->base.ramht); + nvkm_memory_unref(&imem->base.vbios); + nvkm_mm_fini(&imem->heap); + if (imem->iomem) + iounmap(imem->iomem); + return imem; +} + +static const struct nvkm_instmem_func +nv40_instmem = { + .dtor = nv40_instmem_dtor, + .oneinit = nv40_instmem_oneinit, + .rd32 = nv40_instmem_rd32, + .wr32 = nv40_instmem_wr32, + .memory_new = nv40_instobj_new, + .zero = false, +}; + +int +nv40_instmem_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_instmem **pimem) +{ + struct nv40_instmem *imem; + int bar; + + if (!(imem = kzalloc(sizeof(*imem), GFP_KERNEL))) + return -ENOMEM; + nvkm_instmem_ctor(&nv40_instmem, device, type, inst, &imem->base); + *pimem = &imem->base; + + /* map bar */ + if (device->func->resource_size(device, 2)) + bar = 2; + else + bar = 3; + + imem->iomem = ioremap_wc(device->func->resource_addr(device, bar), + device->func->resource_size(device, bar)); + if (!imem->iomem) { + nvkm_error(&imem->base.subdev, "unable to map PRAMIN BAR\n"); + return -EFAULT; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c new file mode 100644 index 000000000..c51bac761 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c @@ -0,0 +1,400 @@ +/* + * 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 + */ +#define nv50_instmem(p) container_of((p), struct nv50_instmem, base) +#include "priv.h" + +#include +#include +#include +#include + +struct nv50_instmem { + struct nvkm_instmem base; + u64 addr; + + /* Mappings that can be evicted when BAR2 space has been exhausted. */ + struct list_head lru; +}; + +/****************************************************************************** + * instmem object implementation + *****************************************************************************/ +#define nv50_instobj(p) container_of((p), struct nv50_instobj, base.memory) + +struct nv50_instobj { + struct nvkm_instobj base; + struct nv50_instmem *imem; + struct nvkm_memory *ram; + struct nvkm_vma *bar; + refcount_t maps; + void *map; + struct list_head lru; +}; + +static void +nv50_instobj_wr32_slow(struct nvkm_memory *memory, u64 offset, u32 data) +{ + struct nv50_instobj *iobj = nv50_instobj(memory); + struct nv50_instmem *imem = iobj->imem; + struct nvkm_device *device = imem->base.subdev.device; + u64 base = (nvkm_memory_addr(iobj->ram) + offset) & 0xffffff00000ULL; + u64 addr = (nvkm_memory_addr(iobj->ram) + offset) & 0x000000fffffULL; + unsigned long flags; + + spin_lock_irqsave(&imem->base.lock, flags); + if (unlikely(imem->addr != base)) { + nvkm_wr32(device, 0x001700, base >> 16); + imem->addr = base; + } + nvkm_wr32(device, 0x700000 + addr, data); + spin_unlock_irqrestore(&imem->base.lock, flags); +} + +static u32 +nv50_instobj_rd32_slow(struct nvkm_memory *memory, u64 offset) +{ + struct nv50_instobj *iobj = nv50_instobj(memory); + struct nv50_instmem *imem = iobj->imem; + struct nvkm_device *device = imem->base.subdev.device; + u64 base = (nvkm_memory_addr(iobj->ram) + offset) & 0xffffff00000ULL; + u64 addr = (nvkm_memory_addr(iobj->ram) + offset) & 0x000000fffffULL; + u32 data; + unsigned long flags; + + spin_lock_irqsave(&imem->base.lock, flags); + if (unlikely(imem->addr != base)) { + nvkm_wr32(device, 0x001700, base >> 16); + imem->addr = base; + } + data = nvkm_rd32(device, 0x700000 + addr); + spin_unlock_irqrestore(&imem->base.lock, flags); + return data; +} + +static const struct nvkm_memory_ptrs +nv50_instobj_slow = { + .rd32 = nv50_instobj_rd32_slow, + .wr32 = nv50_instobj_wr32_slow, +}; + +static void +nv50_instobj_wr32(struct nvkm_memory *memory, u64 offset, u32 data) +{ + iowrite32_native(data, nv50_instobj(memory)->map + offset); +} + +static u32 +nv50_instobj_rd32(struct nvkm_memory *memory, u64 offset) +{ + return ioread32_native(nv50_instobj(memory)->map + offset); +} + +static const struct nvkm_memory_ptrs +nv50_instobj_fast = { + .rd32 = nv50_instobj_rd32, + .wr32 = nv50_instobj_wr32, +}; + +static void +nv50_instobj_kmap(struct nv50_instobj *iobj, struct nvkm_vmm *vmm) +{ + struct nv50_instmem *imem = iobj->imem; + struct nv50_instobj *eobj; + struct nvkm_memory *memory = &iobj->base.memory; + struct nvkm_subdev *subdev = &imem->base.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_vma *bar = NULL, *ebar; + u64 size = nvkm_memory_size(memory); + void *emap; + int ret; + + /* Attempt to allocate BAR2 address-space and map the object + * into it. The lock has to be dropped while doing this due + * to the possibility of recursion for page table allocation. + */ + mutex_unlock(&imem->base.mutex); + while ((ret = nvkm_vmm_get(vmm, 12, size, &bar))) { + /* Evict unused mappings, and keep retrying until we either + * succeed,or there's no more objects left on the LRU. + */ + mutex_lock(&imem->base.mutex); + eobj = list_first_entry_or_null(&imem->lru, typeof(*eobj), lru); + if (eobj) { + nvkm_debug(subdev, "evict %016llx %016llx @ %016llx\n", + nvkm_memory_addr(&eobj->base.memory), + nvkm_memory_size(&eobj->base.memory), + eobj->bar->addr); + list_del_init(&eobj->lru); + ebar = eobj->bar; + eobj->bar = NULL; + emap = eobj->map; + eobj->map = NULL; + } + mutex_unlock(&imem->base.mutex); + if (!eobj) + break; + iounmap(emap); + nvkm_vmm_put(vmm, &ebar); + } + + if (ret == 0) + ret = nvkm_memory_map(memory, 0, vmm, bar, NULL, 0); + mutex_lock(&imem->base.mutex); + if (ret || iobj->bar) { + /* We either failed, or another thread beat us. */ + mutex_unlock(&imem->base.mutex); + nvkm_vmm_put(vmm, &bar); + mutex_lock(&imem->base.mutex); + return; + } + + /* Make the mapping visible to the host. */ + iobj->bar = bar; + iobj->map = ioremap_wc(device->func->resource_addr(device, 3) + + (u32)iobj->bar->addr, size); + if (!iobj->map) { + nvkm_warn(subdev, "PRAMIN ioremap failed\n"); + nvkm_vmm_put(vmm, &iobj->bar); + } +} + +static int +nv50_instobj_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm, + struct nvkm_vma *vma, void *argv, u32 argc) +{ + memory = nv50_instobj(memory)->ram; + return nvkm_memory_map(memory, offset, vmm, vma, argv, argc); +} + +static void +nv50_instobj_release(struct nvkm_memory *memory) +{ + struct nv50_instobj *iobj = nv50_instobj(memory); + struct nv50_instmem *imem = iobj->imem; + struct nvkm_subdev *subdev = &imem->base.subdev; + + wmb(); + nvkm_bar_flush(subdev->device->bar); + + if (refcount_dec_and_mutex_lock(&iobj->maps, &imem->base.mutex)) { + /* Add the now-unused mapping to the LRU instead of directly + * unmapping it here, in case we need to map it again later. + */ + if (likely(iobj->lru.next) && iobj->map) { + BUG_ON(!list_empty(&iobj->lru)); + list_add_tail(&iobj->lru, &imem->lru); + } + + /* Switch back to NULL accessors when last map is gone. */ + iobj->base.memory.ptrs = NULL; + mutex_unlock(&imem->base.mutex); + } +} + +static void __iomem * +nv50_instobj_acquire(struct nvkm_memory *memory) +{ + struct nv50_instobj *iobj = nv50_instobj(memory); + struct nvkm_instmem *imem = &iobj->imem->base; + struct nvkm_vmm *vmm; + void __iomem *map = NULL; + + /* Already mapped? */ + if (refcount_inc_not_zero(&iobj->maps)) + return iobj->map; + + /* Take the lock, and re-check that another thread hasn't + * already mapped the object in the meantime. + */ + mutex_lock(&imem->mutex); + if (refcount_inc_not_zero(&iobj->maps)) { + mutex_unlock(&imem->mutex); + return iobj->map; + } + + /* Attempt to get a direct CPU mapping of the object. */ + if ((vmm = nvkm_bar_bar2_vmm(imem->subdev.device))) { + if (!iobj->map) + nv50_instobj_kmap(iobj, vmm); + map = iobj->map; + } + + if (!refcount_inc_not_zero(&iobj->maps)) { + /* Exclude object from eviction while it's being accessed. */ + if (likely(iobj->lru.next)) + list_del_init(&iobj->lru); + + if (map) + iobj->base.memory.ptrs = &nv50_instobj_fast; + else + iobj->base.memory.ptrs = &nv50_instobj_slow; + refcount_set(&iobj->maps, 1); + } + + mutex_unlock(&imem->mutex); + return map; +} + +static void +nv50_instobj_boot(struct nvkm_memory *memory, struct nvkm_vmm *vmm) +{ + struct nv50_instobj *iobj = nv50_instobj(memory); + struct nvkm_instmem *imem = &iobj->imem->base; + + /* Exclude bootstrapped objects (ie. the page tables for the + * instmem BAR itself) from eviction. + */ + mutex_lock(&imem->mutex); + if (likely(iobj->lru.next)) { + list_del_init(&iobj->lru); + iobj->lru.next = NULL; + } + + nv50_instobj_kmap(iobj, vmm); + nvkm_instmem_boot(imem); + mutex_unlock(&imem->mutex); +} + +static u64 +nv50_instobj_size(struct nvkm_memory *memory) +{ + return nvkm_memory_size(nv50_instobj(memory)->ram); +} + +static u64 +nv50_instobj_addr(struct nvkm_memory *memory) +{ + return nvkm_memory_addr(nv50_instobj(memory)->ram); +} + +static u64 +nv50_instobj_bar2(struct nvkm_memory *memory) +{ + struct nv50_instobj *iobj = nv50_instobj(memory); + u64 addr = ~0ULL; + if (nv50_instobj_acquire(&iobj->base.memory)) { + iobj->lru.next = NULL; /* Exclude from eviction. */ + addr = iobj->bar->addr; + } + nv50_instobj_release(&iobj->base.memory); + return addr; +} + +static enum nvkm_memory_target +nv50_instobj_target(struct nvkm_memory *memory) +{ + return nvkm_memory_target(nv50_instobj(memory)->ram); +} + +static void * +nv50_instobj_dtor(struct nvkm_memory *memory) +{ + struct nv50_instobj *iobj = nv50_instobj(memory); + struct nvkm_instmem *imem = &iobj->imem->base; + struct nvkm_vma *bar; + void *map; + + mutex_lock(&imem->mutex); + if (likely(iobj->lru.next)) + list_del(&iobj->lru); + map = iobj->map; + bar = iobj->bar; + mutex_unlock(&imem->mutex); + + if (map) { + struct nvkm_vmm *vmm = nvkm_bar_bar2_vmm(imem->subdev.device); + iounmap(map); + if (likely(vmm)) /* Can be NULL during BAR destructor. */ + nvkm_vmm_put(vmm, &bar); + } + + nvkm_memory_unref(&iobj->ram); + nvkm_instobj_dtor(imem, &iobj->base); + return iobj; +} + +static const struct nvkm_memory_func +nv50_instobj_func = { + .dtor = nv50_instobj_dtor, + .target = nv50_instobj_target, + .bar2 = nv50_instobj_bar2, + .addr = nv50_instobj_addr, + .size = nv50_instobj_size, + .boot = nv50_instobj_boot, + .acquire = nv50_instobj_acquire, + .release = nv50_instobj_release, + .map = nv50_instobj_map, +}; + +static int +nv50_instobj_new(struct nvkm_instmem *base, u32 size, u32 align, bool zero, + struct nvkm_memory **pmemory) +{ + struct nv50_instmem *imem = nv50_instmem(base); + struct nv50_instobj *iobj; + struct nvkm_device *device = imem->base.subdev.device; + u8 page = max(order_base_2(align), 12); + + if (!(iobj = kzalloc(sizeof(*iobj), GFP_KERNEL))) + return -ENOMEM; + *pmemory = &iobj->base.memory; + + nvkm_instobj_ctor(&nv50_instobj_func, &imem->base, &iobj->base); + iobj->imem = imem; + refcount_set(&iobj->maps, 0); + INIT_LIST_HEAD(&iobj->lru); + + return nvkm_ram_get(device, 0, 1, page, size, true, true, &iobj->ram); +} + +/****************************************************************************** + * instmem subdev implementation + *****************************************************************************/ + +static void +nv50_instmem_fini(struct nvkm_instmem *base) +{ + nv50_instmem(base)->addr = ~0ULL; +} + +static const struct nvkm_instmem_func +nv50_instmem = { + .fini = nv50_instmem_fini, + .memory_new = nv50_instobj_new, + .zero = false, +}; + +int +nv50_instmem_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_instmem **pimem) +{ + struct nv50_instmem *imem; + + if (!(imem = kzalloc(sizeof(*imem), GFP_KERNEL))) + return -ENOMEM; + nvkm_instmem_ctor(&nv50_instmem, device, type, inst, &imem->base); + INIT_LIST_HEAD(&imem->lru); + *pimem = &imem->base; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h new file mode 100644 index 000000000..56c15e30a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_INSTMEM_PRIV_H__ +#define __NVKM_INSTMEM_PRIV_H__ +#define nvkm_instmem(p) container_of((p), struct nvkm_instmem, subdev) +#include + +struct nvkm_instmem_func { + void *(*dtor)(struct nvkm_instmem *); + int (*oneinit)(struct nvkm_instmem *); + void (*fini)(struct nvkm_instmem *); + u32 (*rd32)(struct nvkm_instmem *, u32 addr); + void (*wr32)(struct nvkm_instmem *, u32 addr, u32 data); + int (*memory_new)(struct nvkm_instmem *, u32 size, u32 align, + bool zero, struct nvkm_memory **); + bool zero; +}; + +void nvkm_instmem_ctor(const struct nvkm_instmem_func *, struct nvkm_device *, + enum nvkm_subdev_type, int, struct nvkm_instmem *); +void nvkm_instmem_boot(struct nvkm_instmem *); + +#include + +struct nvkm_instobj { + struct nvkm_memory memory; + struct list_head head; + u32 *suspend; +}; + +void nvkm_instobj_ctor(const struct nvkm_memory_func *func, + struct nvkm_instmem *, struct nvkm_instobj *); +void nvkm_instobj_dtor(struct nvkm_instmem *, struct nvkm_instobj *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild new file mode 100644 index 000000000..728d75010 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/ltc/base.o +nvkm-y += nvkm/subdev/ltc/gf100.o +nvkm-y += nvkm/subdev/ltc/gk104.o +nvkm-y += nvkm/subdev/ltc/gm107.o +nvkm-y += nvkm/subdev/ltc/gm200.o +nvkm-y += nvkm/subdev/ltc/gp100.o +nvkm-y += nvkm/subdev/ltc/gp102.o +nvkm-y += nvkm/subdev/ltc/gp10b.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c new file mode 100644 index 000000000..fa683c190 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c @@ -0,0 +1,143 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +void +nvkm_ltc_tags_clear(struct nvkm_device *device, u32 first, u32 count) +{ + struct nvkm_ltc *ltc = device->ltc; + const u32 limit = first + count - 1; + + BUG_ON((first > limit) || (limit >= ltc->num_tags)); + + mutex_lock(<c->mutex); + ltc->func->cbc_clear(ltc, first, limit); + ltc->func->cbc_wait(ltc); + mutex_unlock(<c->mutex); +} + +int +nvkm_ltc_zbc_color_get(struct nvkm_ltc *ltc, int index, const u32 color[4]) +{ + memcpy(ltc->zbc_color[index], color, sizeof(ltc->zbc_color[index])); + ltc->func->zbc_clear_color(ltc, index, color); + return index; +} + +int +nvkm_ltc_zbc_depth_get(struct nvkm_ltc *ltc, int index, const u32 depth) +{ + ltc->zbc_depth[index] = depth; + ltc->func->zbc_clear_depth(ltc, index, depth); + return index; +} + +int +nvkm_ltc_zbc_stencil_get(struct nvkm_ltc *ltc, int index, const u32 stencil) +{ + ltc->zbc_stencil[index] = stencil; + ltc->func->zbc_clear_stencil(ltc, index, stencil); + return index; +} + +void +nvkm_ltc_invalidate(struct nvkm_ltc *ltc) +{ + if (ltc->func->invalidate) + ltc->func->invalidate(ltc); +} + +void +nvkm_ltc_flush(struct nvkm_ltc *ltc) +{ + if (ltc->func->flush) + ltc->func->flush(ltc); +} + +static void +nvkm_ltc_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_ltc *ltc = nvkm_ltc(subdev); + ltc->func->intr(ltc); +} + +static int +nvkm_ltc_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_ltc *ltc = nvkm_ltc(subdev); + return ltc->func->oneinit(ltc); +} + +static int +nvkm_ltc_init(struct nvkm_subdev *subdev) +{ + struct nvkm_ltc *ltc = nvkm_ltc(subdev); + int i; + + for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) { + ltc->func->zbc_clear_color(ltc, i, ltc->zbc_color[i]); + ltc->func->zbc_clear_depth(ltc, i, ltc->zbc_depth[i]); + if (ltc->func->zbc_clear_stencil) + ltc->func->zbc_clear_stencil(ltc, i, ltc->zbc_stencil[i]); + } + + ltc->func->init(ltc); + return 0; +} + +static void * +nvkm_ltc_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_ltc *ltc = nvkm_ltc(subdev); + nvkm_memory_unref(<c->tag_ram); + mutex_destroy(<c->mutex); + return ltc; +} + +static const struct nvkm_subdev_func +nvkm_ltc = { + .dtor = nvkm_ltc_dtor, + .oneinit = nvkm_ltc_oneinit, + .init = nvkm_ltc_init, + .intr = nvkm_ltc_intr, +}; + +int +nvkm_ltc_new_(const struct nvkm_ltc_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_ltc **pltc) +{ + struct nvkm_ltc *ltc; + + if (!(ltc = *pltc = kzalloc(sizeof(*ltc), GFP_KERNEL))) + return -ENOMEM; + + nvkm_subdev_ctor(&nvkm_ltc, device, type, inst, <c->subdev); + ltc->func = func; + mutex_init(<c->mutex); + ltc->zbc_min = 1; /* reserve 0 for disabled */ + ltc->zbc_max = min(func->zbc, NVKM_LTC_MAX_ZBC_CNT) - 1; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c new file mode 100644 index 000000000..fd8aeafc8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c @@ -0,0 +1,256 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include +#include + +void +gf100_ltc_cbc_clear(struct nvkm_ltc *ltc, u32 start, u32 limit) +{ + struct nvkm_device *device = ltc->subdev.device; + nvkm_wr32(device, 0x17e8cc, start); + nvkm_wr32(device, 0x17e8d0, limit); + nvkm_wr32(device, 0x17e8c8, 0x00000004); +} + +void +gf100_ltc_cbc_wait(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + int c, s; + for (c = 0; c < ltc->ltc_nr; c++) { + for (s = 0; s < ltc->lts_nr; s++) { + const u32 addr = 0x1410c8 + (c * 0x2000) + (s * 0x400); + nvkm_msec(device, 2000, + if (!nvkm_rd32(device, addr)) + break; + ); + } + } +} + +void +gf100_ltc_zbc_clear_color(struct nvkm_ltc *ltc, int i, const u32 color[4]) +{ + struct nvkm_device *device = ltc->subdev.device; + nvkm_mask(device, 0x17ea44, 0x0000000f, i); + nvkm_wr32(device, 0x17ea48, color[0]); + nvkm_wr32(device, 0x17ea4c, color[1]); + nvkm_wr32(device, 0x17ea50, color[2]); + nvkm_wr32(device, 0x17ea54, color[3]); +} + +void +gf100_ltc_zbc_clear_depth(struct nvkm_ltc *ltc, int i, const u32 depth) +{ + struct nvkm_device *device = ltc->subdev.device; + nvkm_mask(device, 0x17ea44, 0x0000000f, i); + nvkm_wr32(device, 0x17ea58, depth); +} + +const struct nvkm_bitfield +gf100_ltc_lts_intr_name[] = { + { 0x00000001, "IDLE_ERROR_IQ" }, + { 0x00000002, "IDLE_ERROR_CBC" }, + { 0x00000004, "IDLE_ERROR_TSTG" }, + { 0x00000008, "IDLE_ERROR_DSTG" }, + { 0x00000010, "EVICTED_CB" }, + { 0x00000020, "ILLEGAL_COMPSTAT" }, + { 0x00000040, "BLOCKLINEAR_CB" }, + { 0x00000100, "ECC_SEC_ERROR" }, + { 0x00000200, "ECC_DED_ERROR" }, + { 0x00000400, "DEBUG" }, + { 0x00000800, "ATOMIC_TO_Z" }, + { 0x00001000, "ILLEGAL_ATOMIC" }, + { 0x00002000, "BLKACTIVITY_ERR" }, + {} +}; + +static void +gf100_ltc_lts_intr(struct nvkm_ltc *ltc, int c, int s) +{ + struct nvkm_subdev *subdev = <c->subdev; + struct nvkm_device *device = subdev->device; + u32 base = 0x141000 + (c * 0x2000) + (s * 0x400); + u32 intr = nvkm_rd32(device, base + 0x020); + u32 stat = intr & 0x0000ffff; + char msg[128]; + + if (stat) { + nvkm_snprintbf(msg, sizeof(msg), gf100_ltc_lts_intr_name, stat); + nvkm_error(subdev, "LTC%d_LTS%d: %08x [%s]\n", c, s, stat, msg); + } + + nvkm_wr32(device, base + 0x020, intr); +} + +void +gf100_ltc_intr(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + u32 mask; + + mask = nvkm_rd32(device, 0x00017c); + while (mask) { + u32 s, c = __ffs(mask); + for (s = 0; s < ltc->lts_nr; s++) + gf100_ltc_lts_intr(ltc, c, s); + mask &= ~(1 << c); + } +} + +void +gf100_ltc_invalidate(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + s64 taken; + + nvkm_wr32(device, 0x70004, 0x00000001); + taken = nvkm_wait_msec(device, 2000, 0x70004, 0x00000003, 0x00000000); + + if (taken > 0) + nvkm_debug(<c->subdev, "LTC invalidate took %lld ns\n", taken); +} + +void +gf100_ltc_flush(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + s64 taken; + + nvkm_wr32(device, 0x70010, 0x00000001); + taken = nvkm_wait_msec(device, 2000, 0x70010, 0x00000003, 0x00000000); + + if (taken > 0) + nvkm_debug(<c->subdev, "LTC flush took %lld ns\n", taken); +} + +/* TODO: Figure out tag memory details and drop the over-cautious allocation. + */ +int +gf100_ltc_oneinit_tag_ram(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + struct nvkm_fb *fb = device->fb; + struct nvkm_ram *ram = fb->ram; + u32 bits = (nvkm_rd32(device, 0x100c80) & 0x00001000) ? 16 : 17; + u32 tag_size, tag_margin, tag_align; + int ret; + + /* No VRAM, no tags for now. */ + if (!ram) { + ltc->num_tags = 0; + goto mm_init; + } + + /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */ + ltc->num_tags = (ram->size >> 17) / 4; + if (ltc->num_tags > (1 << bits)) + ltc->num_tags = 1 << bits; /* we have 16/17 bits in PTE */ + ltc->num_tags = (ltc->num_tags + 63) & ~63; /* round up to 64 */ + + tag_align = ltc->ltc_nr * 0x800; + tag_margin = (tag_align < 0x6000) ? 0x6000 : tag_align; + + /* 4 part 4 sub: 0x2000 bytes for 56 tags */ + /* 3 part 4 sub: 0x6000 bytes for 168 tags */ + /* + * About 147 bytes per tag. Let's be safe and allocate x2, which makes + * 0x4980 bytes for 64 tags, and round up to 0x6000 bytes for 64 tags. + * + * For 4 GiB of memory we'll have 8192 tags which makes 3 MiB, < 0.1 %. + */ + tag_size = (ltc->num_tags / 64) * 0x6000 + tag_margin; + tag_size += tag_align; + + ret = nvkm_ram_get(device, NVKM_RAM_MM_NORMAL, 0x01, 12, tag_size, + true, true, <c->tag_ram); + if (ret) { + ltc->num_tags = 0; + } else { + u64 tag_base = nvkm_memory_addr(ltc->tag_ram) + tag_margin; + + tag_base += tag_align - 1; + do_div(tag_base, tag_align); + + ltc->tag_base = tag_base; + } + +mm_init: + nvkm_mm_fini(&fb->tags.mm); + return nvkm_mm_init(&fb->tags.mm, 0, 0, ltc->num_tags, 1); +} + +int +gf100_ltc_oneinit(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + const u32 parts = nvkm_rd32(device, 0x022438); + const u32 mask = nvkm_rd32(device, 0x022554); + const u32 slice = nvkm_rd32(device, 0x17e8dc) >> 28; + int i; + + for (i = 0; i < parts; i++) { + if (!(mask & (1 << i))) + ltc->ltc_nr++; + } + ltc->lts_nr = slice; + + return gf100_ltc_oneinit_tag_ram(ltc); +} + +static void +gf100_ltc_init(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + u32 lpg128 = !(nvkm_rd32(device, 0x100c80) & 0x00000001); + + nvkm_mask(device, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */ + nvkm_wr32(device, 0x17e8d8, ltc->ltc_nr); + nvkm_wr32(device, 0x17e8d4, ltc->tag_base); + nvkm_mask(device, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000); +} + +static const struct nvkm_ltc_func +gf100_ltc = { + .oneinit = gf100_ltc_oneinit, + .init = gf100_ltc_init, + .intr = gf100_ltc_intr, + .cbc_clear = gf100_ltc_cbc_clear, + .cbc_wait = gf100_ltc_cbc_wait, + .zbc = 16, + .zbc_clear_color = gf100_ltc_zbc_clear_color, + .zbc_clear_depth = gf100_ltc_zbc_clear_depth, + .invalidate = gf100_ltc_invalidate, + .flush = gf100_ltc_flush, +}; + +int +gf100_ltc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_ltc **pltc) +{ + return nvkm_ltc_new_(&gf100_ltc, device, type, inst, pltc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c new file mode 100644 index 000000000..94aa09244 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c @@ -0,0 +1,57 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static void +gk104_ltc_init(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + u32 lpg128 = !(nvkm_rd32(device, 0x100c80) & 0x00000001); + + nvkm_wr32(device, 0x17e8d8, ltc->ltc_nr); + nvkm_wr32(device, 0x17e000, ltc->ltc_nr); + nvkm_wr32(device, 0x17e8d4, ltc->tag_base); + nvkm_mask(device, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000); +} + +static const struct nvkm_ltc_func +gk104_ltc = { + .oneinit = gf100_ltc_oneinit, + .init = gk104_ltc_init, + .intr = gf100_ltc_intr, + .cbc_clear = gf100_ltc_cbc_clear, + .cbc_wait = gf100_ltc_cbc_wait, + .zbc = 16, + .zbc_clear_color = gf100_ltc_zbc_clear_color, + .zbc_clear_depth = gf100_ltc_zbc_clear_depth, + .invalidate = gf100_ltc_invalidate, + .flush = gf100_ltc_flush, +}; + +int +gk104_ltc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_ltc **pltc) +{ + return nvkm_ltc_new_(&gk104_ltc, device, type, inst, pltc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c new file mode 100644 index 000000000..54d1d65d5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c @@ -0,0 +1,152 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +void +gm107_ltc_cbc_clear(struct nvkm_ltc *ltc, u32 start, u32 limit) +{ + struct nvkm_device *device = ltc->subdev.device; + nvkm_wr32(device, 0x17e270, start); + nvkm_wr32(device, 0x17e274, limit); + nvkm_mask(device, 0x17e26c, 0x00000000, 0x00000004); +} + +void +gm107_ltc_cbc_wait(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + int c, s; + for (c = 0; c < ltc->ltc_nr; c++) { + for (s = 0; s < ltc->lts_nr; s++) { + const u32 addr = 0x14046c + (c * 0x2000) + (s * 0x200); + nvkm_wait_msec(device, 2000, addr, + 0x00000004, 0x00000000); + } + } +} + +void +gm107_ltc_zbc_clear_color(struct nvkm_ltc *ltc, int i, const u32 color[4]) +{ + struct nvkm_device *device = ltc->subdev.device; + nvkm_mask(device, 0x17e338, 0x0000000f, i); + nvkm_wr32(device, 0x17e33c, color[0]); + nvkm_wr32(device, 0x17e340, color[1]); + nvkm_wr32(device, 0x17e344, color[2]); + nvkm_wr32(device, 0x17e348, color[3]); +} + +void +gm107_ltc_zbc_clear_depth(struct nvkm_ltc *ltc, int i, const u32 depth) +{ + struct nvkm_device *device = ltc->subdev.device; + nvkm_mask(device, 0x17e338, 0x0000000f, i); + nvkm_wr32(device, 0x17e34c, depth); +} + +void +gm107_ltc_intr_lts(struct nvkm_ltc *ltc, int c, int s) +{ + struct nvkm_subdev *subdev = <c->subdev; + struct nvkm_device *device = subdev->device; + u32 base = 0x140400 + (c * 0x2000) + (s * 0x200); + u32 intr = nvkm_rd32(device, base + 0x00c); + u16 stat = intr & 0x0000ffff; + char msg[128]; + + if (stat) { + nvkm_snprintbf(msg, sizeof(msg), gf100_ltc_lts_intr_name, stat); + nvkm_error(subdev, "LTC%d_LTS%d: %08x [%s]\n", c, s, intr, msg); + } + + nvkm_wr32(device, base + 0x00c, intr); +} + +void +gm107_ltc_intr(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + u32 mask; + + mask = nvkm_rd32(device, 0x00017c); + while (mask) { + u32 s, c = __ffs(mask); + for (s = 0; s < ltc->lts_nr; s++) + gm107_ltc_intr_lts(ltc, c, s); + mask &= ~(1 << c); + } +} + +static int +gm107_ltc_oneinit(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + const u32 parts = nvkm_rd32(device, 0x022438); + const u32 mask = nvkm_rd32(device, 0x021c14); + const u32 slice = nvkm_rd32(device, 0x17e280) >> 28; + int i; + + for (i = 0; i < parts; i++) { + if (!(mask & (1 << i))) + ltc->ltc_nr++; + } + ltc->lts_nr = slice; + + return gf100_ltc_oneinit_tag_ram(ltc); +} + +static void +gm107_ltc_init(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + u32 lpg128 = !(nvkm_rd32(device, 0x100c80) & 0x00000001); + + nvkm_wr32(device, 0x17e27c, ltc->ltc_nr); + nvkm_wr32(device, 0x17e278, ltc->tag_base); + nvkm_mask(device, 0x17e264, 0x00000002, lpg128 ? 0x00000002 : 0x00000000); +} + +static const struct nvkm_ltc_func +gm107_ltc = { + .oneinit = gm107_ltc_oneinit, + .init = gm107_ltc_init, + .intr = gm107_ltc_intr, + .cbc_clear = gm107_ltc_cbc_clear, + .cbc_wait = gm107_ltc_cbc_wait, + .zbc = 16, + .zbc_clear_color = gm107_ltc_zbc_clear_color, + .zbc_clear_depth = gm107_ltc_zbc_clear_depth, + .invalidate = gf100_ltc_invalidate, + .flush = gf100_ltc_flush, +}; + +int +gm107_ltc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_ltc **pltc) +{ + return nvkm_ltc_new_(&gm107_ltc, device, type, inst, pltc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm200.c new file mode 100644 index 000000000..8cfdbbdd8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm200.c @@ -0,0 +1,64 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +static int +gm200_ltc_oneinit(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + + ltc->ltc_nr = nvkm_rd32(device, 0x12006c); + ltc->lts_nr = nvkm_rd32(device, 0x17e280) >> 28; + + return gf100_ltc_oneinit_tag_ram(ltc); +} +static void +gm200_ltc_init(struct nvkm_ltc *ltc) +{ + nvkm_wr32(ltc->subdev.device, 0x17e278, ltc->tag_base); +} + +static const struct nvkm_ltc_func +gm200_ltc = { + .oneinit = gm200_ltc_oneinit, + .init = gm200_ltc_init, + .intr = gm107_ltc_intr, + .cbc_clear = gm107_ltc_cbc_clear, + .cbc_wait = gm107_ltc_cbc_wait, + .zbc = 16, + .zbc_clear_color = gm107_ltc_zbc_clear_color, + .zbc_clear_depth = gm107_ltc_zbc_clear_depth, + .invalidate = gf100_ltc_invalidate, + .flush = gf100_ltc_flush, +}; + +int +gm200_ltc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_ltc **pltc) +{ + return nvkm_ltc_new_(&gm200_ltc, device, type, inst, pltc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp100.c new file mode 100644 index 000000000..a4a6cd9b4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp100.c @@ -0,0 +1,76 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +void +gp100_ltc_intr(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + u32 mask; + + mask = nvkm_rd32(device, 0x0001c0); + while (mask) { + u32 s, c = __ffs(mask); + for (s = 0; s < ltc->lts_nr; s++) + gm107_ltc_intr_lts(ltc, c, s); + mask &= ~(1 << c); + } +} + +int +gp100_ltc_oneinit(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + ltc->ltc_nr = nvkm_rd32(device, 0x12006c); + ltc->lts_nr = nvkm_rd32(device, 0x17e280) >> 28; + /*XXX: tagram allocation - TBD */ + return 0; +} + +void +gp100_ltc_init(struct nvkm_ltc *ltc) +{ + /*XXX: PMU LS call to setup tagram address */ +} + +static const struct nvkm_ltc_func +gp100_ltc = { + .oneinit = gp100_ltc_oneinit, + .init = gp100_ltc_init, + .intr = gp100_ltc_intr, + .cbc_clear = gm107_ltc_cbc_clear, + .cbc_wait = gm107_ltc_cbc_wait, + .zbc = 16, + .zbc_clear_color = gm107_ltc_zbc_clear_color, + .zbc_clear_depth = gm107_ltc_zbc_clear_depth, + .invalidate = gf100_ltc_invalidate, + .flush = gf100_ltc_flush, +}; + +int +gp100_ltc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_ltc **pltc) +{ + return nvkm_ltc_new_(&gp100_ltc, device, type, inst, pltc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp102.c new file mode 100644 index 000000000..ff05d617e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp102.c @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +void +gp102_ltc_zbc_clear_stencil(struct nvkm_ltc *ltc, int i, const u32 stencil) +{ + struct nvkm_device *device = ltc->subdev.device; + nvkm_mask(device, 0x17e338, 0x0000000f, i); + nvkm_wr32(device, 0x17e204, stencil); +} + +static const struct nvkm_ltc_func +gp102_ltc = { + .oneinit = gp100_ltc_oneinit, + .init = gp100_ltc_init, + .intr = gp100_ltc_intr, + .cbc_clear = gm107_ltc_cbc_clear, + .cbc_wait = gm107_ltc_cbc_wait, + .zbc = 16, + .zbc_clear_color = gm107_ltc_zbc_clear_color, + .zbc_clear_depth = gm107_ltc_zbc_clear_depth, + .zbc_clear_stencil = gp102_ltc_zbc_clear_stencil, + .invalidate = gf100_ltc_invalidate, + .flush = gf100_ltc_flush, +}; + +int +gp102_ltc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_ltc **pltc) +{ + return nvkm_ltc_new_(&gp102_ltc, device, type, inst, pltc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c new file mode 100644 index 000000000..dfebd796c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 NVIDIA Corporation. + * + * 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: Thierry Reding + */ + +#include "priv.h" + +static void +gp10b_ltc_init(struct nvkm_ltc *ltc) +{ + struct nvkm_device *device = ltc->subdev.device; + struct iommu_fwspec *spec; + + nvkm_wr32(device, 0x17e27c, ltc->ltc_nr); + nvkm_wr32(device, 0x17e000, ltc->ltc_nr); + nvkm_wr32(device, 0x100800, ltc->ltc_nr); + + spec = dev_iommu_fwspec_get(device->dev); + if (spec) { + u32 sid = spec->ids[0] & 0xffff; + + /* stream ID */ + nvkm_wr32(device, 0x160000, sid << 2); + } +} + +static const struct nvkm_ltc_func +gp10b_ltc = { + .oneinit = gp100_ltc_oneinit, + .init = gp10b_ltc_init, + .intr = gp100_ltc_intr, + .cbc_clear = gm107_ltc_cbc_clear, + .cbc_wait = gm107_ltc_cbc_wait, + .zbc = 16, + .zbc_clear_color = gm107_ltc_zbc_clear_color, + .zbc_clear_depth = gm107_ltc_zbc_clear_depth, + .zbc_clear_stencil = gp102_ltc_zbc_clear_stencil, + .invalidate = gf100_ltc_invalidate, + .flush = gf100_ltc_flush, +}; + +int +gp10b_ltc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_ltc **pltc) +{ + return nvkm_ltc_new_(&gp10b_ltc, device, type, inst, pltc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h new file mode 100644 index 000000000..2bebe1390 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_LTC_PRIV_H__ +#define __NVKM_LTC_PRIV_H__ +#define nvkm_ltc(p) container_of((p), struct nvkm_ltc, subdev) +#include +#include + +int nvkm_ltc_new_(const struct nvkm_ltc_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_ltc **); + +struct nvkm_ltc_func { + int (*oneinit)(struct nvkm_ltc *); + void (*init)(struct nvkm_ltc *); + void (*intr)(struct nvkm_ltc *); + + void (*cbc_clear)(struct nvkm_ltc *, u32 start, u32 limit); + void (*cbc_wait)(struct nvkm_ltc *); + + int zbc; + void (*zbc_clear_color)(struct nvkm_ltc *, int, const u32[4]); + void (*zbc_clear_depth)(struct nvkm_ltc *, int, const u32); + void (*zbc_clear_stencil)(struct nvkm_ltc *, int, const u32); + + void (*invalidate)(struct nvkm_ltc *); + void (*flush)(struct nvkm_ltc *); +}; + +int gf100_ltc_oneinit(struct nvkm_ltc *); +int gf100_ltc_oneinit_tag_ram(struct nvkm_ltc *); +void gf100_ltc_intr(struct nvkm_ltc *); +void gf100_ltc_cbc_clear(struct nvkm_ltc *, u32, u32); +void gf100_ltc_cbc_wait(struct nvkm_ltc *); +void gf100_ltc_zbc_clear_color(struct nvkm_ltc *, int, const u32[4]); +void gf100_ltc_zbc_clear_depth(struct nvkm_ltc *, int, const u32); +void gf100_ltc_invalidate(struct nvkm_ltc *); +void gf100_ltc_flush(struct nvkm_ltc *); +extern const struct nvkm_bitfield gf100_ltc_lts_intr_name[]; + +void gm107_ltc_intr(struct nvkm_ltc *); +void gm107_ltc_intr_lts(struct nvkm_ltc *, int ltc, int lts); +void gm107_ltc_cbc_clear(struct nvkm_ltc *, u32, u32); +void gm107_ltc_cbc_wait(struct nvkm_ltc *); +void gm107_ltc_zbc_clear_color(struct nvkm_ltc *, int, const u32[4]); +void gm107_ltc_zbc_clear_depth(struct nvkm_ltc *, int, const u32); + +int gp100_ltc_oneinit(struct nvkm_ltc *); +void gp100_ltc_init(struct nvkm_ltc *); +void gp100_ltc_intr(struct nvkm_ltc *); + +void gp102_ltc_zbc_clear_stencil(struct nvkm_ltc *, int, const u32); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild new file mode 100644 index 000000000..ac2b34e9a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/mc/base.o +nvkm-y += nvkm/subdev/mc/nv04.o +nvkm-y += nvkm/subdev/mc/nv11.o +nvkm-y += nvkm/subdev/mc/nv17.o +nvkm-y += nvkm/subdev/mc/nv44.o +nvkm-y += nvkm/subdev/mc/nv50.o +nvkm-y += nvkm/subdev/mc/g84.o +nvkm-y += nvkm/subdev/mc/g98.o +nvkm-y += nvkm/subdev/mc/gt215.o +nvkm-y += nvkm/subdev/mc/gf100.o +nvkm-y += nvkm/subdev/mc/gk104.o +nvkm-y += nvkm/subdev/mc/gk20a.o +nvkm-y += nvkm/subdev/mc/gp100.o +nvkm-y += nvkm/subdev/mc/gp10b.o +nvkm-y += nvkm/subdev/mc/tu102.o +nvkm-y += nvkm/subdev/mc/ga100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c new file mode 100644 index 000000000..21c4af3f8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c @@ -0,0 +1,227 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +void +nvkm_mc_unk260(struct nvkm_device *device, u32 data) +{ + struct nvkm_mc *mc = device->mc; + if (likely(mc) && mc->func->unk260) + mc->func->unk260(mc, data); +} + +void +nvkm_mc_intr_mask(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, bool en) +{ + struct nvkm_mc *mc = device->mc; + const struct nvkm_mc_map *map; + if (likely(mc) && mc->func->intr_mask) { + u32 mask = nvkm_top_intr_mask(device, type, inst); + for (map = mc->func->intr; !mask && map->stat; map++) { + if (map->type == type && map->inst == inst) + mask = map->stat; + } + mc->func->intr_mask(mc, mask, en ? mask : 0); + } +} + +void +nvkm_mc_intr_unarm(struct nvkm_device *device) +{ + struct nvkm_mc *mc = device->mc; + if (likely(mc)) + mc->func->intr_unarm(mc); +} + +void +nvkm_mc_intr_rearm(struct nvkm_device *device) +{ + struct nvkm_mc *mc = device->mc; + if (likely(mc)) + mc->func->intr_rearm(mc); +} + +static u32 +nvkm_mc_intr_stat(struct nvkm_mc *mc) +{ + u32 intr = mc->func->intr_stat(mc); + if (WARN_ON_ONCE(intr == 0xffffffff)) + intr = 0; /* likely fallen off the bus */ + return intr; +} + +void +nvkm_mc_intr(struct nvkm_device *device, bool *handled) +{ + struct nvkm_mc *mc = device->mc; + struct nvkm_top *top = device->top; + struct nvkm_top_device *tdev; + struct nvkm_subdev *subdev; + const struct nvkm_mc_map *map; + u32 stat, intr; + + if (unlikely(!mc)) + return; + + stat = intr = nvkm_mc_intr_stat(mc); + + if (top) { + list_for_each_entry(tdev, &top->device, head) { + if (tdev->intr >= 0 && (stat & BIT(tdev->intr))) { + subdev = nvkm_device_subdev(device, tdev->type, tdev->inst); + if (subdev) { + nvkm_subdev_intr(subdev); + stat &= ~BIT(tdev->intr); + if (!stat) + break; + } + } + } + } + + for (map = mc->func->intr; map->stat; map++) { + if (intr & map->stat) { + subdev = nvkm_device_subdev(device, map->type, map->inst); + if (subdev) + nvkm_subdev_intr(subdev); + stat &= ~map->stat; + } + } + + if (stat) + nvkm_error(&mc->subdev, "intr %08x\n", stat); + *handled = intr != 0; +} + +static u32 +nvkm_mc_reset_mask(struct nvkm_device *device, bool isauto, enum nvkm_subdev_type type, int inst) +{ + struct nvkm_mc *mc = device->mc; + const struct nvkm_mc_map *map; + u64 pmc_enable = 0; + if (likely(mc)) { + if (!(pmc_enable = nvkm_top_reset(device, type, inst))) { + for (map = mc->func->reset; map && map->stat; map++) { + if (!isauto || !map->noauto) { + if (map->type == type && map->inst == inst) { + pmc_enable = map->stat; + break; + } + } + } + } + } + return pmc_enable; +} + +void +nvkm_mc_reset(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + u64 pmc_enable = nvkm_mc_reset_mask(device, true, type, inst); + if (pmc_enable) { + nvkm_mask(device, 0x000200, pmc_enable, 0x00000000); + nvkm_mask(device, 0x000200, pmc_enable, pmc_enable); + nvkm_rd32(device, 0x000200); + } +} + +void +nvkm_mc_disable(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + u64 pmc_enable = nvkm_mc_reset_mask(device, false, type, inst); + if (pmc_enable) + nvkm_mask(device, 0x000200, pmc_enable, 0x00000000); +} + +void +nvkm_mc_enable(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + u64 pmc_enable = nvkm_mc_reset_mask(device, false, type, inst); + if (pmc_enable) { + nvkm_mask(device, 0x000200, pmc_enable, pmc_enable); + nvkm_rd32(device, 0x000200); + } +} + +bool +nvkm_mc_enabled(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + u64 pmc_enable = nvkm_mc_reset_mask(device, false, type, inst); + + return (pmc_enable != 0) && + ((nvkm_rd32(device, 0x000200) & pmc_enable) == pmc_enable); +} + + +static int +nvkm_mc_fini(struct nvkm_subdev *subdev, bool suspend) +{ + nvkm_mc_intr_unarm(subdev->device); + return 0; +} + +static int +nvkm_mc_init(struct nvkm_subdev *subdev) +{ + struct nvkm_mc *mc = nvkm_mc(subdev); + if (mc->func->init) + mc->func->init(mc); + nvkm_mc_intr_rearm(subdev->device); + return 0; +} + +static void * +nvkm_mc_dtor(struct nvkm_subdev *subdev) +{ + return nvkm_mc(subdev); +} + +static const struct nvkm_subdev_func +nvkm_mc = { + .dtor = nvkm_mc_dtor, + .init = nvkm_mc_init, + .fini = nvkm_mc_fini, +}; + +void +nvkm_mc_ctor(const struct nvkm_mc_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_mc *mc) +{ + nvkm_subdev_ctor(&nvkm_mc, device, type, inst, &mc->subdev); + mc->func = func; +} + +int +nvkm_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + struct nvkm_mc *mc; + if (!(mc = *pmc = kzalloc(sizeof(*mc), GFP_KERNEL))) + return -ENOMEM; + nvkm_mc_ctor(func, device, type, inst, *pmc); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g84.c new file mode 100644 index 000000000..4cfc1c984 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g84.c @@ -0,0 +1,68 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_mc_map +g84_mc_reset[] = { + { 0x04008000, NVKM_ENGINE_BSP }, + { 0x02004000, NVKM_ENGINE_CIPHER }, + { 0x01020000, NVKM_ENGINE_VP }, + { 0x00400002, NVKM_ENGINE_MPEG }, + { 0x00201000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + {} +}; + +static const struct nvkm_mc_map +g84_mc_intr[] = { + { 0x04000000, NVKM_ENGINE_DISP }, + { 0x00020000, NVKM_ENGINE_VP }, + { 0x00008000, NVKM_ENGINE_BSP }, + { 0x00004000, NVKM_ENGINE_CIPHER }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000001, NVKM_ENGINE_MPEG }, + { 0x0002d101, NVKM_SUBDEV_FB }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x00200000, NVKM_SUBDEV_GPIO }, + { 0x00200000, NVKM_SUBDEV_I2C }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + {}, +}; + +static const struct nvkm_mc_func +g84_mc = { + .init = nv50_mc_init, + .intr = g84_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_stat = nv04_mc_intr_stat, + .reset = g84_mc_reset, +}; + +int +g84_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&g84_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c new file mode 100644 index 000000000..b7e58d75d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c @@ -0,0 +1,68 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_mc_map +g98_mc_reset[] = { + { 0x04008000, NVKM_ENGINE_MSVLD }, + { 0x02004000, NVKM_ENGINE_SEC }, + { 0x01020000, NVKM_ENGINE_MSPDEC }, + { 0x00400002, NVKM_ENGINE_MSPPP }, + { 0x00201000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + {} +}; + +static const struct nvkm_mc_map +g98_mc_intr[] = { + { 0x04000000, NVKM_ENGINE_DISP }, + { 0x00020000, NVKM_ENGINE_MSPDEC }, + { 0x00008000, NVKM_ENGINE_MSVLD }, + { 0x00004000, NVKM_ENGINE_SEC }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000001, NVKM_ENGINE_MSPPP }, + { 0x0002d101, NVKM_SUBDEV_FB }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x00200000, NVKM_SUBDEV_GPIO }, + { 0x00200000, NVKM_SUBDEV_I2C }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + {}, +}; + +static const struct nvkm_mc_func +g98_mc = { + .init = nv50_mc_init, + .intr = g98_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_stat = nv04_mc_intr_stat, + .reset = g98_mc_reset, +}; + +int +g98_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&g98_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c new file mode 100644 index 000000000..4105175df --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c @@ -0,0 +1,74 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +static void +ga100_mc_intr_unarm(struct nvkm_mc *mc) +{ + nvkm_wr32(mc->subdev.device, 0xb81610, 0x00000004); +} + +static void +ga100_mc_intr_rearm(struct nvkm_mc *mc) +{ + nvkm_wr32(mc->subdev.device, 0xb81608, 0x00000004); +} + +static void +ga100_mc_intr_mask(struct nvkm_mc *mc, u32 mask, u32 intr) +{ + nvkm_wr32(mc->subdev.device, 0xb81210, mask & intr ); + nvkm_wr32(mc->subdev.device, 0xb81410, mask & ~(mask & intr)); +} + +static u32 +ga100_mc_intr_stat(struct nvkm_mc *mc) +{ + u32 intr_top = nvkm_rd32(mc->subdev.device, 0xb81600), intr = 0x00000000; + if (intr_top & 0x00000004) + intr = nvkm_mask(mc->subdev.device, 0xb81010, 0x00000000, 0x00000000); + return intr; +} + +static void +ga100_mc_init(struct nvkm_mc *mc) +{ + nv50_mc_init(mc); + nvkm_wr32(mc->subdev.device, 0xb81210, 0xffffffff); +} + +static const struct nvkm_mc_func +ga100_mc = { + .init = ga100_mc_init, + .intr = gp100_mc_intr, + .intr_unarm = ga100_mc_intr_unarm, + .intr_rearm = ga100_mc_intr_rearm, + .intr_mask = ga100_mc_intr_mask, + .intr_stat = ga100_mc_intr_stat, + .reset = gk104_mc_reset, +}; + +int +ga100_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&ga100_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c new file mode 100644 index 000000000..3a589c6f7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c @@ -0,0 +1,118 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_mc_map +gf100_mc_reset[] = { + { 0x00020000, NVKM_ENGINE_MSPDEC }, + { 0x00008000, NVKM_ENGINE_MSVLD }, + { 0x00002000, NVKM_SUBDEV_PMU, 0, true }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000080, NVKM_ENGINE_CE, 1 }, + { 0x00000040, NVKM_ENGINE_CE, 0 }, + { 0x00000002, NVKM_ENGINE_MSPPP }, + {} +}; + +static const struct nvkm_mc_map +gf100_mc_intr[] = { + { 0x04000000, NVKM_ENGINE_DISP }, + { 0x00020000, NVKM_ENGINE_MSPDEC }, + { 0x00008000, NVKM_ENGINE_MSVLD }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000040, NVKM_ENGINE_CE, 1 }, + { 0x00000020, NVKM_ENGINE_CE, 0 }, + { 0x00000001, NVKM_ENGINE_MSPPP }, + { 0x40000000, NVKM_SUBDEV_PRIVRING }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x08000000, NVKM_SUBDEV_FB }, + { 0x02000000, NVKM_SUBDEV_LTC }, + { 0x01000000, NVKM_SUBDEV_PMU }, + { 0x00200000, NVKM_SUBDEV_GPIO }, + { 0x00200000, NVKM_SUBDEV_I2C }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + { 0x00040000, NVKM_SUBDEV_THERM }, + { 0x00002000, NVKM_SUBDEV_FB }, + {}, +}; + +void +gf100_mc_intr_unarm(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_wr32(device, 0x000140, 0x00000000); + nvkm_wr32(device, 0x000144, 0x00000000); + nvkm_rd32(device, 0x000140); +} + +void +gf100_mc_intr_rearm(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_wr32(device, 0x000140, 0x00000001); + nvkm_wr32(device, 0x000144, 0x00000001); +} + +u32 +gf100_mc_intr_stat(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + u32 intr0 = nvkm_rd32(device, 0x000100); + u32 intr1 = nvkm_rd32(device, 0x000104); + return intr0 | intr1; +} + +void +gf100_mc_intr_mask(struct nvkm_mc *mc, u32 mask, u32 stat) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_mask(device, 0x000640, mask, stat); + nvkm_mask(device, 0x000644, mask, stat); +} + +void +gf100_mc_unk260(struct nvkm_mc *mc, u32 data) +{ + nvkm_wr32(mc->subdev.device, 0x000260, data); +} + +static const struct nvkm_mc_func +gf100_mc = { + .init = nv50_mc_init, + .intr = gf100_mc_intr, + .intr_unarm = gf100_mc_intr_unarm, + .intr_rearm = gf100_mc_intr_rearm, + .intr_mask = gf100_mc_intr_mask, + .intr_stat = gf100_mc_intr_stat, + .reset = gf100_mc_reset, + .unk260 = gf100_mc_unk260, +}; + +int +gf100_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&gf100_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk104.c new file mode 100644 index 000000000..d9b9067fa --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk104.c @@ -0,0 +1,66 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +const struct nvkm_mc_map +gk104_mc_reset[] = { + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00002000, NVKM_SUBDEV_PMU, 0, true }, + {} +}; + +const struct nvkm_mc_map +gk104_mc_intr[] = { + { 0x04000000, NVKM_ENGINE_DISP }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x40000000, NVKM_SUBDEV_PRIVRING }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x08000000, NVKM_SUBDEV_FB }, + { 0x02000000, NVKM_SUBDEV_LTC }, + { 0x01000000, NVKM_SUBDEV_PMU }, + { 0x00200000, NVKM_SUBDEV_GPIO }, + { 0x00200000, NVKM_SUBDEV_I2C }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + { 0x00040000, NVKM_SUBDEV_THERM }, + { 0x00002000, NVKM_SUBDEV_FB }, + {}, +}; + +static const struct nvkm_mc_func +gk104_mc = { + .init = nv50_mc_init, + .intr = gk104_mc_intr, + .intr_unarm = gf100_mc_intr_unarm, + .intr_rearm = gf100_mc_intr_rearm, + .intr_mask = gf100_mc_intr_mask, + .intr_stat = gf100_mc_intr_stat, + .reset = gk104_mc_reset, + .unk260 = gf100_mc_unk260, +}; + +int +gk104_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&gk104_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c new file mode 100644 index 000000000..035902927 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_mc_func +gk20a_mc = { + .init = nv50_mc_init, + .intr = gk104_mc_intr, + .intr_unarm = gf100_mc_intr_unarm, + .intr_rearm = gf100_mc_intr_rearm, + .intr_mask = gf100_mc_intr_mask, + .intr_stat = gf100_mc_intr_stat, + .reset = gk104_mc_reset, +}; + +int +gk20a_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&gk20a_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp100.c new file mode 100644 index 000000000..5fd1a0595 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp100.c @@ -0,0 +1,128 @@ +/* + * 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 + */ +#define gp100_mc(p) container_of((p), struct gp100_mc, base) +#include "priv.h" + +struct gp100_mc { + struct nvkm_mc base; + spinlock_t lock; + bool intr; + u32 mask; +}; + +static void +gp100_mc_intr_update(struct gp100_mc *mc) +{ + struct nvkm_device *device = mc->base.subdev.device; + u32 mask = mc->intr ? mc->mask : 0, i; + for (i = 0; i < 2; i++) { + nvkm_wr32(device, 0x000180 + (i * 0x04), ~mask); + nvkm_wr32(device, 0x000160 + (i * 0x04), mask); + } +} + +void +gp100_mc_intr_unarm(struct nvkm_mc *base) +{ + struct gp100_mc *mc = gp100_mc(base); + unsigned long flags; + spin_lock_irqsave(&mc->lock, flags); + mc->intr = false; + gp100_mc_intr_update(mc); + spin_unlock_irqrestore(&mc->lock, flags); +} + +void +gp100_mc_intr_rearm(struct nvkm_mc *base) +{ + struct gp100_mc *mc = gp100_mc(base); + unsigned long flags; + spin_lock_irqsave(&mc->lock, flags); + mc->intr = true; + gp100_mc_intr_update(mc); + spin_unlock_irqrestore(&mc->lock, flags); +} + +void +gp100_mc_intr_mask(struct nvkm_mc *base, u32 mask, u32 intr) +{ + struct gp100_mc *mc = gp100_mc(base); + unsigned long flags; + spin_lock_irqsave(&mc->lock, flags); + mc->mask = (mc->mask & ~mask) | intr; + gp100_mc_intr_update(mc); + spin_unlock_irqrestore(&mc->lock, flags); +} + +const struct nvkm_mc_map +gp100_mc_intr[] = { + { 0x04000000, NVKM_ENGINE_DISP }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000200, NVKM_SUBDEV_FAULT }, + { 0x40000000, NVKM_SUBDEV_PRIVRING }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x08000000, NVKM_SUBDEV_FB }, + { 0x02000000, NVKM_SUBDEV_LTC }, + { 0x01000000, NVKM_SUBDEV_PMU }, + { 0x00200000, NVKM_SUBDEV_GPIO }, + { 0x00200000, NVKM_SUBDEV_I2C }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + { 0x00040000, NVKM_SUBDEV_THERM }, + { 0x00002000, NVKM_SUBDEV_FB }, + {}, +}; + +static const struct nvkm_mc_func +gp100_mc = { + .init = nv50_mc_init, + .intr = gp100_mc_intr, + .intr_unarm = gp100_mc_intr_unarm, + .intr_rearm = gp100_mc_intr_rearm, + .intr_mask = gp100_mc_intr_mask, + .intr_stat = gf100_mc_intr_stat, + .reset = gk104_mc_reset, +}; + +int +gp100_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + struct gp100_mc *mc; + + if (!(mc = kzalloc(sizeof(*mc), GFP_KERNEL))) + return -ENOMEM; + nvkm_mc_ctor(func, device, type, inst, &mc->base); + *pmc = &mc->base; + + spin_lock_init(&mc->lock); + mc->intr = false; + mc->mask = 0x7fffffff; + return 0; +} + +int +gp100_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return gp100_mc_new_(&gp100_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp10b.c new file mode 100644 index 000000000..dd581d030 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp10b.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "priv.h" + +static void +gp10b_mc_init(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_wr32(device, 0x000200, 0xffffffff); /* everything on */ + nvkm_wr32(device, 0x00020c, 0xffffffff); /* everything out of ELPG */ +} + +static const struct nvkm_mc_func +gp10b_mc = { + .init = gp10b_mc_init, + .intr = gp100_mc_intr, + .intr_unarm = gp100_mc_intr_unarm, + .intr_rearm = gp100_mc_intr_rearm, + .intr_mask = gp100_mc_intr_mask, + .intr_stat = gf100_mc_intr_stat, + .reset = gk104_mc_reset, +}; + +int +gp10b_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return gp100_mc_new_(&gp10b_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gt215.c new file mode 100644 index 000000000..1b4d43531 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gt215.c @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_mc_map +gt215_mc_reset[] = { + { 0x04008000, NVKM_ENGINE_MSVLD }, + { 0x01020000, NVKM_ENGINE_MSPDEC }, + { 0x00802000, NVKM_ENGINE_CE, 0 }, + { 0x00400002, NVKM_ENGINE_MSPPP }, + { 0x00201000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + {} +}; + +static const struct nvkm_mc_map +gt215_mc_intr[] = { + { 0x04000000, NVKM_ENGINE_DISP }, + { 0x00400000, NVKM_ENGINE_CE, 0 }, + { 0x00020000, NVKM_ENGINE_MSPDEC }, + { 0x00008000, NVKM_ENGINE_MSVLD }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000001, NVKM_ENGINE_MSPPP }, + { 0x00429101, NVKM_SUBDEV_FB }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x00200000, NVKM_SUBDEV_GPIO }, + { 0x00200000, NVKM_SUBDEV_I2C }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + { 0x00080000, NVKM_SUBDEV_THERM }, + { 0x00040000, NVKM_SUBDEV_PMU }, + {}, +}; + +static void +gt215_mc_intr_mask(struct nvkm_mc *mc, u32 mask, u32 stat) +{ + nvkm_mask(mc->subdev.device, 0x000640, mask, stat); +} + +static const struct nvkm_mc_func +gt215_mc = { + .init = nv50_mc_init, + .intr = gt215_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_mask = gt215_mc_intr_mask, + .intr_stat = nv04_mc_intr_stat, + .reset = gt215_mc_reset, +}; + +int +gt215_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(>215_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c new file mode 100644 index 000000000..bc0d09baf --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c @@ -0,0 +1,86 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +const struct nvkm_mc_map +nv04_mc_reset[] = { + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + {} +}; + +static const struct nvkm_mc_map +nv04_mc_intr[] = { + { 0x01010000, NVKM_ENGINE_DISP }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + {} +}; + +void +nv04_mc_intr_unarm(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_wr32(device, 0x000140, 0x00000000); + nvkm_rd32(device, 0x000140); +} + +void +nv04_mc_intr_rearm(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_wr32(device, 0x000140, 0x00000001); +} + +u32 +nv04_mc_intr_stat(struct nvkm_mc *mc) +{ + return nvkm_rd32(mc->subdev.device, 0x000100); +} + +void +nv04_mc_init(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_wr32(device, 0x000200, 0xffffffff); /* everything enabled */ + nvkm_wr32(device, 0x001850, 0x00000001); /* disable rom access */ +} + +static const struct nvkm_mc_func +nv04_mc = { + .init = nv04_mc_init, + .intr = nv04_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_stat = nv04_mc_intr_stat, + .reset = nv04_mc_reset, +}; + +int +nv04_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&nv04_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv11.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv11.c new file mode 100644 index 000000000..ab59ca1ee --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv11.c @@ -0,0 +1,50 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_mc_map +nv11_mc_intr[] = { + { 0x03010000, NVKM_ENGINE_DISP }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + {} +}; + +static const struct nvkm_mc_func +nv11_mc = { + .init = nv04_mc_init, + .intr = nv11_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_stat = nv04_mc_intr_stat, + .reset = nv04_mc_reset, +}; + +int +nv11_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&nv11_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv17.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv17.c new file mode 100644 index 000000000..03d756e26 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv17.c @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +const struct nvkm_mc_map +nv17_mc_reset[] = { + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000002, NVKM_ENGINE_MPEG }, + {} +}; + +const struct nvkm_mc_map +nv17_mc_intr[] = { + { 0x03010000, NVKM_ENGINE_DISP }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000001, NVKM_ENGINE_MPEG }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + {} +}; + +static const struct nvkm_mc_func +nv17_mc = { + .init = nv04_mc_init, + .intr = nv17_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_stat = nv04_mc_intr_stat, + .reset = nv17_mc_reset, +}; + +int +nv17_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&nv17_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c new file mode 100644 index 000000000..95f65766e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c @@ -0,0 +1,54 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +void +nv44_mc_init(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + u32 tmp = nvkm_rd32(device, 0x10020c); + + nvkm_wr32(device, 0x000200, 0xffffffff); /* everything enabled */ + + nvkm_wr32(device, 0x001700, tmp); + nvkm_wr32(device, 0x001704, 0); + nvkm_wr32(device, 0x001708, 0); + nvkm_wr32(device, 0x00170c, tmp); +} + +static const struct nvkm_mc_func +nv44_mc = { + .init = nv44_mc_init, + .intr = nv17_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_stat = nv04_mc_intr_stat, + .reset = nv17_mc_reset, +}; + +int +nv44_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&nv44_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c new file mode 100644 index 000000000..fce3613cd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c @@ -0,0 +1,61 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_mc_map +nv50_mc_intr[] = { + { 0x04000000, NVKM_ENGINE_DISP }, + { 0x00001000, NVKM_ENGINE_GR }, + { 0x00000100, NVKM_ENGINE_FIFO }, + { 0x00000001, NVKM_ENGINE_MPEG }, + { 0x00001101, NVKM_SUBDEV_FB }, + { 0x10000000, NVKM_SUBDEV_BUS }, + { 0x00200000, NVKM_SUBDEV_GPIO }, + { 0x00200000, NVKM_SUBDEV_I2C }, + { 0x00100000, NVKM_SUBDEV_TIMER }, + {}, +}; + +void +nv50_mc_init(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + nvkm_wr32(device, 0x000200, 0xffffffff); /* everything on */ +} + +static const struct nvkm_mc_func +nv50_mc = { + .init = nv50_mc_init, + .intr = nv50_mc_intr, + .intr_unarm = nv04_mc_intr_unarm, + .intr_rearm = nv04_mc_intr_rearm, + .intr_stat = nv04_mc_intr_stat, + .reset = nv17_mc_reset, +}; + +int +nv50_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return nvkm_mc_new_(&nv50_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h new file mode 100644 index 000000000..c8bcabb98 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_MC_PRIV_H__ +#define __NVKM_MC_PRIV_H__ +#define nvkm_mc(p) container_of((p), struct nvkm_mc, subdev) +#include + +void nvkm_mc_ctor(const struct nvkm_mc_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_mc *); +int nvkm_mc_new_(const struct nvkm_mc_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_mc **); + +struct nvkm_mc_map { + u32 stat; + enum nvkm_subdev_type type; + int inst; + bool noauto; +}; + +struct nvkm_mc_func { + void (*init)(struct nvkm_mc *); + const struct nvkm_mc_map *intr; + /* disable reporting of interrupts to host */ + void (*intr_unarm)(struct nvkm_mc *); + /* enable reporting of interrupts to host */ + void (*intr_rearm)(struct nvkm_mc *); + /* (un)mask delivery of specific interrupts */ + void (*intr_mask)(struct nvkm_mc *, u32 mask, u32 stat); + /* retrieve pending interrupt mask (NV_PMC_INTR) */ + u32 (*intr_stat)(struct nvkm_mc *); + const struct nvkm_mc_map *reset; + void (*unk260)(struct nvkm_mc *, u32); +}; + +void nv04_mc_init(struct nvkm_mc *); +void nv04_mc_intr_unarm(struct nvkm_mc *); +void nv04_mc_intr_rearm(struct nvkm_mc *); +u32 nv04_mc_intr_stat(struct nvkm_mc *); +extern const struct nvkm_mc_map nv04_mc_reset[]; + +extern const struct nvkm_mc_map nv17_mc_intr[]; +extern const struct nvkm_mc_map nv17_mc_reset[]; + +void nv44_mc_init(struct nvkm_mc *); + +void nv50_mc_init(struct nvkm_mc *); +void gk104_mc_init(struct nvkm_mc *); + +void gf100_mc_intr_unarm(struct nvkm_mc *); +void gf100_mc_intr_rearm(struct nvkm_mc *); +void gf100_mc_intr_mask(struct nvkm_mc *, u32, u32); +u32 gf100_mc_intr_stat(struct nvkm_mc *); +void gf100_mc_unk260(struct nvkm_mc *, u32); +void gp100_mc_intr_unarm(struct nvkm_mc *); +void gp100_mc_intr_rearm(struct nvkm_mc *); +void gp100_mc_intr_mask(struct nvkm_mc *, u32, u32); +int gp100_mc_new_(const struct nvkm_mc_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_mc **); + +extern const struct nvkm_mc_map gk104_mc_intr[]; +extern const struct nvkm_mc_map gk104_mc_reset[]; + +extern const struct nvkm_mc_map gp100_mc_intr[]; +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/tu102.c new file mode 100644 index 000000000..a96084b34 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/tu102.c @@ -0,0 +1,136 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#define tu102_mc(p) container_of((p), struct tu102_mc, base) +#include "priv.h" + +struct tu102_mc { + struct nvkm_mc base; + spinlock_t lock; + bool intr; + u32 mask; +}; + +static void +tu102_mc_intr_update(struct tu102_mc *mc) +{ + struct nvkm_device *device = mc->base.subdev.device; + u32 mask = mc->intr ? mc->mask : 0, i; + + for (i = 0; i < 2; i++) { + nvkm_wr32(device, 0x000180 + (i * 0x04), ~mask); + nvkm_wr32(device, 0x000160 + (i * 0x04), mask); + } + + if (mask & 0x00000200) + nvkm_wr32(device, 0xb81608, 0x6); + else + nvkm_wr32(device, 0xb81610, 0x6); +} + +static void +tu102_mc_intr_unarm(struct nvkm_mc *base) +{ + struct tu102_mc *mc = tu102_mc(base); + unsigned long flags; + + spin_lock_irqsave(&mc->lock, flags); + mc->intr = false; + tu102_mc_intr_update(mc); + spin_unlock_irqrestore(&mc->lock, flags); +} + +static void +tu102_mc_intr_rearm(struct nvkm_mc *base) +{ + struct tu102_mc *mc = tu102_mc(base); + unsigned long flags; + + spin_lock_irqsave(&mc->lock, flags); + mc->intr = true; + tu102_mc_intr_update(mc); + spin_unlock_irqrestore(&mc->lock, flags); +} + +static void +tu102_mc_intr_mask(struct nvkm_mc *base, u32 mask, u32 intr) +{ + struct tu102_mc *mc = tu102_mc(base); + unsigned long flags; + + spin_lock_irqsave(&mc->lock, flags); + mc->mask = (mc->mask & ~mask) | intr; + tu102_mc_intr_update(mc); + spin_unlock_irqrestore(&mc->lock, flags); +} + +static u32 +tu102_mc_intr_stat(struct nvkm_mc *mc) +{ + struct nvkm_device *device = mc->subdev.device; + u32 intr0 = nvkm_rd32(device, 0x000100); + u32 intr1 = nvkm_rd32(device, 0x000104); + u32 intr_top = nvkm_rd32(device, 0xb81600); + + /* Turing and above route the MMU fault interrupts via a different + * interrupt tree with different control registers. For the moment remap + * them back to the old PMC vector. + */ + if (intr_top & 0x00000006) + intr0 |= 0x00000200; + + return intr0 | intr1; +} + + +static const struct nvkm_mc_func +tu102_mc = { + .init = nv50_mc_init, + .intr = gp100_mc_intr, + .intr_unarm = tu102_mc_intr_unarm, + .intr_rearm = tu102_mc_intr_rearm, + .intr_mask = tu102_mc_intr_mask, + .intr_stat = tu102_mc_intr_stat, + .reset = gk104_mc_reset, +}; + +static int +tu102_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + struct tu102_mc *mc; + + if (!(mc = kzalloc(sizeof(*mc), GFP_KERNEL))) + return -ENOMEM; + nvkm_mc_ctor(func, device, type, inst, &mc->base); + *pmc = &mc->base; + + spin_lock_init(&mc->lock); + mc->intr = false; + mc->mask = 0x7fffffff; + return 0; +} + +int +tu102_mc_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_mc **pmc) +{ + return tu102_mc_new_(&tu102_mc, device, type, inst, pmc); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild new file mode 100644 index 000000000..a602b0cb5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/mmu/base.o +nvkm-y += nvkm/subdev/mmu/nv04.o +nvkm-y += nvkm/subdev/mmu/nv41.o +nvkm-y += nvkm/subdev/mmu/nv44.o +nvkm-y += nvkm/subdev/mmu/nv50.o +nvkm-y += nvkm/subdev/mmu/g84.o +nvkm-y += nvkm/subdev/mmu/mcp77.o +nvkm-y += nvkm/subdev/mmu/gf100.o +nvkm-y += nvkm/subdev/mmu/gk104.o +nvkm-y += nvkm/subdev/mmu/gk20a.o +nvkm-y += nvkm/subdev/mmu/gm200.o +nvkm-y += nvkm/subdev/mmu/gm20b.o +nvkm-y += nvkm/subdev/mmu/gp100.o +nvkm-y += nvkm/subdev/mmu/gp10b.o +nvkm-y += nvkm/subdev/mmu/gv100.o +nvkm-y += nvkm/subdev/mmu/tu102.o + +nvkm-y += nvkm/subdev/mmu/mem.o +nvkm-y += nvkm/subdev/mmu/memnv04.o +nvkm-y += nvkm/subdev/mmu/memnv50.o +nvkm-y += nvkm/subdev/mmu/memgf100.o + +nvkm-y += nvkm/subdev/mmu/vmm.o +nvkm-y += nvkm/subdev/mmu/vmmnv04.o +nvkm-y += nvkm/subdev/mmu/vmmnv41.o +nvkm-y += nvkm/subdev/mmu/vmmnv44.o +nvkm-y += nvkm/subdev/mmu/vmmnv50.o +nvkm-y += nvkm/subdev/mmu/vmmmcp77.o +nvkm-y += nvkm/subdev/mmu/vmmgf100.o +nvkm-y += nvkm/subdev/mmu/vmmgk104.o +nvkm-y += nvkm/subdev/mmu/vmmgk20a.o +nvkm-y += nvkm/subdev/mmu/vmmgm200.o +nvkm-y += nvkm/subdev/mmu/vmmgm20b.o +nvkm-y += nvkm/subdev/mmu/vmmgp100.o +nvkm-y += nvkm/subdev/mmu/vmmgp10b.o +nvkm-y += nvkm/subdev/mmu/vmmgv100.o +nvkm-y += nvkm/subdev/mmu/vmmtu102.o + +nvkm-y += nvkm/subdev/mmu/umem.o +nvkm-y += nvkm/subdev/mmu/ummu.o +nvkm-y += nvkm/subdev/mmu/uvmm.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c new file mode 100644 index 000000000..ad3b44a9e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c @@ -0,0 +1,437 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "ummu.h" +#include "vmm.h" + +#include +#include + +#include +#include + +struct nvkm_mmu_ptp { + struct nvkm_mmu_pt *pt; + struct list_head head; + u8 shift; + u16 mask; + u16 free; +}; + +static void +nvkm_mmu_ptp_put(struct nvkm_mmu *mmu, bool force, struct nvkm_mmu_pt *pt) +{ + const int slot = pt->base >> pt->ptp->shift; + struct nvkm_mmu_ptp *ptp = pt->ptp; + + /* If there were no free slots in the parent allocation before, + * there will be now, so return PTP to the cache. + */ + if (!ptp->free) + list_add(&ptp->head, &mmu->ptp.list); + ptp->free |= BIT(slot); + + /* If there's no more sub-allocations, destroy PTP. */ + if (ptp->free == ptp->mask) { + nvkm_mmu_ptc_put(mmu, force, &ptp->pt); + list_del(&ptp->head); + kfree(ptp); + } + + kfree(pt); +} + +static struct nvkm_mmu_pt * +nvkm_mmu_ptp_get(struct nvkm_mmu *mmu, u32 size, bool zero) +{ + struct nvkm_mmu_pt *pt; + struct nvkm_mmu_ptp *ptp; + int slot; + + if (!(pt = kzalloc(sizeof(*pt), GFP_KERNEL))) + return NULL; + + ptp = list_first_entry_or_null(&mmu->ptp.list, typeof(*ptp), head); + if (!ptp) { + /* Need to allocate a new parent to sub-allocate from. */ + if (!(ptp = kmalloc(sizeof(*ptp), GFP_KERNEL))) { + kfree(pt); + return NULL; + } + + ptp->pt = nvkm_mmu_ptc_get(mmu, 0x1000, 0x1000, false); + if (!ptp->pt) { + kfree(ptp); + kfree(pt); + return NULL; + } + + ptp->shift = order_base_2(size); + slot = nvkm_memory_size(ptp->pt->memory) >> ptp->shift; + ptp->mask = (1 << slot) - 1; + ptp->free = ptp->mask; + list_add(&ptp->head, &mmu->ptp.list); + } + pt->ptp = ptp; + pt->sub = true; + + /* Sub-allocate from parent object, removing PTP from cache + * if there's no more free slots left. + */ + slot = __ffs(ptp->free); + ptp->free &= ~BIT(slot); + if (!ptp->free) + list_del(&ptp->head); + + pt->memory = pt->ptp->pt->memory; + pt->base = slot << ptp->shift; + pt->addr = pt->ptp->pt->addr + pt->base; + return pt; +} + +struct nvkm_mmu_ptc { + struct list_head head; + struct list_head item; + u32 size; + u32 refs; +}; + +static inline struct nvkm_mmu_ptc * +nvkm_mmu_ptc_find(struct nvkm_mmu *mmu, u32 size) +{ + struct nvkm_mmu_ptc *ptc; + + list_for_each_entry(ptc, &mmu->ptc.list, head) { + if (ptc->size == size) + return ptc; + } + + ptc = kmalloc(sizeof(*ptc), GFP_KERNEL); + if (ptc) { + INIT_LIST_HEAD(&ptc->item); + ptc->size = size; + ptc->refs = 0; + list_add(&ptc->head, &mmu->ptc.list); + } + + return ptc; +} + +void +nvkm_mmu_ptc_put(struct nvkm_mmu *mmu, bool force, struct nvkm_mmu_pt **ppt) +{ + struct nvkm_mmu_pt *pt = *ppt; + if (pt) { + /* Handle sub-allocated page tables. */ + if (pt->sub) { + mutex_lock(&mmu->ptp.mutex); + nvkm_mmu_ptp_put(mmu, force, pt); + mutex_unlock(&mmu->ptp.mutex); + return; + } + + /* Either cache or free the object. */ + mutex_lock(&mmu->ptc.mutex); + if (pt->ptc->refs < 8 /* Heuristic. */ && !force) { + list_add_tail(&pt->head, &pt->ptc->item); + pt->ptc->refs++; + } else { + nvkm_memory_unref(&pt->memory); + kfree(pt); + } + mutex_unlock(&mmu->ptc.mutex); + } +} + +struct nvkm_mmu_pt * +nvkm_mmu_ptc_get(struct nvkm_mmu *mmu, u32 size, u32 align, bool zero) +{ + struct nvkm_mmu_ptc *ptc; + struct nvkm_mmu_pt *pt; + int ret; + + /* Sub-allocated page table (ie. GP100 LPT). */ + if (align < 0x1000) { + mutex_lock(&mmu->ptp.mutex); + pt = nvkm_mmu_ptp_get(mmu, align, zero); + mutex_unlock(&mmu->ptp.mutex); + return pt; + } + + /* Lookup cache for this page table size. */ + mutex_lock(&mmu->ptc.mutex); + ptc = nvkm_mmu_ptc_find(mmu, size); + if (!ptc) { + mutex_unlock(&mmu->ptc.mutex); + return NULL; + } + + /* If there's a free PT in the cache, reuse it. */ + pt = list_first_entry_or_null(&ptc->item, typeof(*pt), head); + if (pt) { + if (zero) + nvkm_fo64(pt->memory, 0, 0, size >> 3); + list_del(&pt->head); + ptc->refs--; + mutex_unlock(&mmu->ptc.mutex); + return pt; + } + mutex_unlock(&mmu->ptc.mutex); + + /* No such luck, we need to allocate. */ + if (!(pt = kmalloc(sizeof(*pt), GFP_KERNEL))) + return NULL; + pt->ptc = ptc; + pt->sub = false; + + ret = nvkm_memory_new(mmu->subdev.device, NVKM_MEM_TARGET_INST, + size, align, zero, &pt->memory); + if (ret) { + kfree(pt); + return NULL; + } + + pt->base = 0; + pt->addr = nvkm_memory_addr(pt->memory); + return pt; +} + +void +nvkm_mmu_ptc_dump(struct nvkm_mmu *mmu) +{ + struct nvkm_mmu_ptc *ptc; + list_for_each_entry(ptc, &mmu->ptc.list, head) { + struct nvkm_mmu_pt *pt, *tt; + list_for_each_entry_safe(pt, tt, &ptc->item, head) { + nvkm_memory_unref(&pt->memory); + list_del(&pt->head); + kfree(pt); + } + } +} + +static void +nvkm_mmu_ptc_fini(struct nvkm_mmu *mmu) +{ + struct nvkm_mmu_ptc *ptc, *ptct; + + list_for_each_entry_safe(ptc, ptct, &mmu->ptc.list, head) { + WARN_ON(!list_empty(&ptc->item)); + list_del(&ptc->head); + kfree(ptc); + } +} + +static void +nvkm_mmu_ptc_init(struct nvkm_mmu *mmu) +{ + mutex_init(&mmu->ptc.mutex); + INIT_LIST_HEAD(&mmu->ptc.list); + mutex_init(&mmu->ptp.mutex); + INIT_LIST_HEAD(&mmu->ptp.list); +} + +static void +nvkm_mmu_type(struct nvkm_mmu *mmu, int heap, u8 type) +{ + if (heap >= 0 && !WARN_ON(mmu->type_nr == ARRAY_SIZE(mmu->type))) { + mmu->type[mmu->type_nr].type = type | mmu->heap[heap].type; + mmu->type[mmu->type_nr].heap = heap; + mmu->type_nr++; + } +} + +static int +nvkm_mmu_heap(struct nvkm_mmu *mmu, u8 type, u64 size) +{ + if (size) { + if (!WARN_ON(mmu->heap_nr == ARRAY_SIZE(mmu->heap))) { + mmu->heap[mmu->heap_nr].type = type; + mmu->heap[mmu->heap_nr].size = size; + return mmu->heap_nr++; + } + } + return -EINVAL; +} + +static void +nvkm_mmu_host(struct nvkm_mmu *mmu) +{ + struct nvkm_device *device = mmu->subdev.device; + u8 type = NVKM_MEM_KIND * !!mmu->func->kind_sys; + int heap; + + /* Non-mappable system memory. */ + heap = nvkm_mmu_heap(mmu, NVKM_MEM_HOST, ~0ULL); + nvkm_mmu_type(mmu, heap, type); + + /* Non-coherent, cached, system memory. + * + * Block-linear mappings of system memory must be done through + * BAR1, and cannot be supported on systems where we're unable + * to map BAR1 with write-combining. + */ + type |= NVKM_MEM_MAPPABLE; + if (!device->bar || device->bar->iomap_uncached) + nvkm_mmu_type(mmu, heap, type & ~NVKM_MEM_KIND); + else + nvkm_mmu_type(mmu, heap, type); + + /* Coherent, cached, system memory. + * + * Unsupported on systems that aren't able to support snooped + * mappings, and also for block-linear mappings which must be + * done through BAR1. + */ + type |= NVKM_MEM_COHERENT; + if (device->func->cpu_coherent) + nvkm_mmu_type(mmu, heap, type & ~NVKM_MEM_KIND); + + /* Uncached system memory. */ + nvkm_mmu_type(mmu, heap, type |= NVKM_MEM_UNCACHED); +} + +static void +nvkm_mmu_vram(struct nvkm_mmu *mmu) +{ + struct nvkm_device *device = mmu->subdev.device; + struct nvkm_mm *mm = &device->fb->ram->vram; + const u64 sizeN = nvkm_mm_heap_size(mm, NVKM_RAM_MM_NORMAL); + const u64 sizeU = nvkm_mm_heap_size(mm, NVKM_RAM_MM_NOMAP); + const u64 sizeM = nvkm_mm_heap_size(mm, NVKM_RAM_MM_MIXED); + u8 type = NVKM_MEM_KIND * !!mmu->func->kind; + u8 heap = NVKM_MEM_VRAM; + int heapM, heapN, heapU; + + /* Mixed-memory doesn't support compression or display. */ + heapM = nvkm_mmu_heap(mmu, heap, sizeM << NVKM_RAM_MM_SHIFT); + + heap |= NVKM_MEM_COMP; + heap |= NVKM_MEM_DISP; + heapN = nvkm_mmu_heap(mmu, heap, sizeN << NVKM_RAM_MM_SHIFT); + heapU = nvkm_mmu_heap(mmu, heap, sizeU << NVKM_RAM_MM_SHIFT); + + /* Add non-mappable VRAM types first so that they're preferred + * over anything else. Mixed-memory will be slower than other + * heaps, it's prioritised last. + */ + nvkm_mmu_type(mmu, heapU, type); + nvkm_mmu_type(mmu, heapN, type); + nvkm_mmu_type(mmu, heapM, type); + + /* Add host memory types next, under the assumption that users + * wanting mappable memory want to use them as staging buffers + * or the like. + */ + nvkm_mmu_host(mmu); + + /* Mappable VRAM types go last, as they're basically the worst + * possible type to ask for unless there's no other choice. + */ + if (device->bar) { + /* Write-combined BAR1 access. */ + type |= NVKM_MEM_MAPPABLE; + if (!device->bar->iomap_uncached) { + nvkm_mmu_type(mmu, heapN, type); + nvkm_mmu_type(mmu, heapM, type); + } + + /* Uncached BAR1 access. */ + type |= NVKM_MEM_COHERENT; + type |= NVKM_MEM_UNCACHED; + nvkm_mmu_type(mmu, heapN, type); + nvkm_mmu_type(mmu, heapM, type); + } +} + +static int +nvkm_mmu_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_mmu *mmu = nvkm_mmu(subdev); + + /* Determine available memory types. */ + if (mmu->subdev.device->fb && mmu->subdev.device->fb->ram) + nvkm_mmu_vram(mmu); + else + nvkm_mmu_host(mmu); + + if (mmu->func->vmm.global) { + int ret = nvkm_vmm_new(subdev->device, 0, 0, NULL, 0, NULL, + "gart", &mmu->vmm); + if (ret) + return ret; + } + + return 0; +} + +static int +nvkm_mmu_init(struct nvkm_subdev *subdev) +{ + struct nvkm_mmu *mmu = nvkm_mmu(subdev); + if (mmu->func->init) + mmu->func->init(mmu); + return 0; +} + +static void * +nvkm_mmu_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_mmu *mmu = nvkm_mmu(subdev); + + nvkm_vmm_unref(&mmu->vmm); + + nvkm_mmu_ptc_fini(mmu); + mutex_destroy(&mmu->mutex); + return mmu; +} + +static const struct nvkm_subdev_func +nvkm_mmu = { + .dtor = nvkm_mmu_dtor, + .oneinit = nvkm_mmu_oneinit, + .init = nvkm_mmu_init, +}; + +void +nvkm_mmu_ctor(const struct nvkm_mmu_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_mmu *mmu) +{ + nvkm_subdev_ctor(&nvkm_mmu, device, type, inst, &mmu->subdev); + mmu->func = func; + mmu->dma_bits = func->dma_bits; + nvkm_mmu_ptc_init(mmu); + mutex_init(&mmu->mutex); + mmu->user.ctor = nvkm_ummu_new; + mmu->user.base = func->mmu.user; +} + +int +nvkm_mmu_new_(const struct nvkm_mmu_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_mmu **pmmu) +{ + if (!(*pmmu = kzalloc(sizeof(**pmmu), GFP_KERNEL))) + return -ENOMEM; + nvkm_mmu_ctor(func, device, type, inst, *pmmu); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/g84.c new file mode 100644 index 000000000..ce47a3b97 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/g84.c @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +static const struct nvkm_mmu_func +g84_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_NV50}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_NV50}, nv50_mem_new, nv50_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_NV50}, nv50_vmm_new, false, 0x0200 }, + .kind = nv50_mmu_kind, + .kind_sys = true, +}; + +int +g84_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&g84_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c new file mode 100644 index 000000000..7a28b1d49 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c @@ -0,0 +1,91 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "mem.h" +#include "vmm.h" + +#include + +/* Map from compressed to corresponding uncompressed storage type. + * The value 0xff represents an invalid storage type. + */ +const u8 * +gf100_mmu_kind(struct nvkm_mmu *mmu, int *count, u8 *invalid) +{ + static const u8 + kind[256] = { + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, /* 0x00 */ + 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, /* 0x10 */ + 0x11, 0x11, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x27, /* 0x20 */ + 0x28, 0x29, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 */ + 0xff, 0xff, 0x26, 0x27, 0x28, 0x29, 0x26, 0x27, + 0x28, 0x29, 0xff, 0xff, 0xff, 0xff, 0x46, 0xff, /* 0x40 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x46, 0x46, 0x46, 0x46, 0xff, 0xff, 0xff, /* 0x50 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70 */ + 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7b, /* 0x80 */ + 0x7b, 0x7b, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xa7, /* 0xa0 */ + 0xa8, 0xa9, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, + 0xa8, 0xa9, 0xaa, 0xc3, 0xff, 0xff, 0xff, 0xff, /* 0xc0 */ + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xc3, 0xc3, + 0xc3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0 */ + 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, + 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, /* 0xe0 */ + 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf0 */ + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfd, 0xfe, 0xff + }; + + *count = ARRAY_SIZE(kind); + *invalid = 0xff; + return kind; +} + +static const struct nvkm_mmu_func +gf100_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_GF100}, gf100_mem_new, gf100_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_GF100}, gf100_vmm_new }, + .kind = gf100_mmu_kind, + .kind_sys = true, +}; + +int +gf100_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&gf100_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk104.c new file mode 100644 index 000000000..34c9b2b82 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk104.c @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +static const struct nvkm_mmu_func +gk104_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_GF100}, gf100_mem_new, gf100_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_GF100}, gk104_vmm_new }, + .kind = gf100_mmu_kind, + .kind_sys = true, +}; + +int +gk104_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&gk104_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk20a.c new file mode 100644 index 000000000..a7db29c42 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gk20a.c @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +static const struct nvkm_mmu_func +gk20a_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, -1, NVIF_CLASS_MEM_GF100}, .umap = gf100_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_GF100}, gk20a_vmm_new }, + .kind = gf100_mmu_kind, + .kind_sys = true, +}; + +int +gk20a_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&gk20a_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm200.c new file mode 100644 index 000000000..e1696f637 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm200.c @@ -0,0 +1,99 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +#include + +const u8 * +gm200_mmu_kind(struct nvkm_mmu *mmu, int *count, u8 *invalid) +{ + static const u8 + kind[256] = { + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, /* 0x00 */ + 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, /* 0x10 */ + 0x11, 0x11, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x27, /* 0x20 */ + 0x28, 0x29, 0x2a, 0x2b, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 */ + 0xff, 0xff, 0x26, 0x27, 0x28, 0x29, 0x26, 0x27, + 0x28, 0x29, 0xff, 0xff, 0xff, 0xff, 0x46, 0xff, /* 0x40 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x46, 0x46, 0x46, 0x46, 0xff, 0xff, 0xff, /* 0x50 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70 */ + 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7b, /* 0x80 */ + 0x7b, 0x7b, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xa7, /* 0xa0 */ + 0xa8, 0xa9, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, + 0xa8, 0xa9, 0xaa, 0xc3, 0xff, 0xff, 0xff, 0xff, /* 0xc0 */ + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xc3, 0xc3, + 0xc3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0 */ + 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, + 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, /* 0xe0 */ + 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf0 */ + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfd, 0xfe, 0xff + }; + *count = ARRAY_SIZE(kind); + *invalid = 0xff; + return kind; +} + +static const struct nvkm_mmu_func +gm200_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_GF100}, gf100_mem_new, gf100_mem_map }, + .vmm = {{ -1, 0, NVIF_CLASS_VMM_GM200}, gm200_vmm_new }, + .kind = gm200_mmu_kind, + .kind_sys = true, +}; + +static const struct nvkm_mmu_func +gm200_mmu_fixed = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_GF100}, gf100_mem_new, gf100_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_GM200}, gm200_vmm_new_fixed }, + .kind = gm200_mmu_kind, + .kind_sys = true, +}; + +int +gm200_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + if (device->fb->page) + return nvkm_mmu_new_(&gm200_mmu_fixed, device, type, inst, pmmu); + return nvkm_mmu_new_(&gm200_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm20b.c new file mode 100644 index 000000000..e6e1a8ad7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gm20b.c @@ -0,0 +1,56 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +#include + +static const struct nvkm_mmu_func +gm20b_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, -1, NVIF_CLASS_MEM_GF100}, .umap = gf100_mem_map }, + .vmm = {{ -1, 0, NVIF_CLASS_VMM_GM200}, gm20b_vmm_new }, + .kind = gm200_mmu_kind, + .kind_sys = true, +}; + +static const struct nvkm_mmu_func +gm20b_mmu_fixed = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, -1, NVIF_CLASS_MEM_GF100}, .umap = gf100_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_GM200}, gm20b_vmm_new_fixed }, + .kind = gm200_mmu_kind, + .kind_sys = true, +}; + +int +gm20b_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + if (device->fb->page) + return nvkm_mmu_new_(&gm20b_mmu_fixed, device, type, inst, pmmu); + return nvkm_mmu_new_(&gm20b_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp100.c new file mode 100644 index 000000000..daa5ab0f8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp100.c @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +#include + +static const struct nvkm_mmu_func +gp100_mmu = { + .dma_bits = 47, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_GF100}, gf100_mem_new, gf100_mem_map }, + .vmm = {{ -1, 0, NVIF_CLASS_VMM_GP100}, gp100_vmm_new }, + .kind = gm200_mmu_kind, + .kind_sys = true, +}; + +int +gp100_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + if (!nvkm_boolopt(device->cfgopt, "GP100MmuLayout", true)) + return gm200_mmu_new(device, type, inst, pmmu); + return nvkm_mmu_new_(&gp100_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp10b.c new file mode 100644 index 000000000..edd0bf9a5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gp10b.c @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +#include + +static const struct nvkm_mmu_func +gp10b_mmu = { + .dma_bits = 47, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, -1, NVIF_CLASS_MEM_GF100}, .umap = gf100_mem_map }, + .vmm = {{ -1, 0, NVIF_CLASS_VMM_GP100}, gp10b_vmm_new }, + .kind = gm200_mmu_kind, + .kind_sys = true, +}; + +int +gp10b_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + if (!nvkm_boolopt(device->cfgopt, "GP100MmuLayout", true)) + return gm20b_mmu_new(device, type, inst, pmmu); + return nvkm_mmu_new_(&gp10b_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gv100.c new file mode 100644 index 000000000..fb8bdc88d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gv100.c @@ -0,0 +1,44 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +#include + +static const struct nvkm_mmu_func +gv100_mmu = { + .dma_bits = 47, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_GF100}, gf100_mem_new, gf100_mem_map }, + .vmm = {{ -1, 0, NVIF_CLASS_VMM_GP100}, gv100_vmm_new }, + .kind = gm200_mmu_kind, + .kind_sys = true, +}; + +int +gv100_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&gv100_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mcp77.c new file mode 100644 index 000000000..514876d64 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mcp77.c @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +static const struct nvkm_mmu_func +mcp77_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_NV50}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_NV50}, nv50_mem_new, nv50_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_NV50}, mcp77_vmm_new, false, 0x0200 }, + .kind = nv50_mmu_kind, + .kind_sys = true, +}; + +int +mcp77_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&mcp77_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.c new file mode 100644 index 000000000..92e363dbb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.c @@ -0,0 +1,242 @@ +/* + * Copyright 2017 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. + */ +#define nvkm_mem(p) container_of((p), struct nvkm_mem, memory) +#include "mem.h" + +#include + +#include +#include + +struct nvkm_mem { + struct nvkm_memory memory; + enum nvkm_memory_target target; + struct nvkm_mmu *mmu; + u64 pages; + struct page **mem; + union { + struct scatterlist *sgl; + dma_addr_t *dma; + }; +}; + +static enum nvkm_memory_target +nvkm_mem_target(struct nvkm_memory *memory) +{ + return nvkm_mem(memory)->target; +} + +static u8 +nvkm_mem_page(struct nvkm_memory *memory) +{ + return PAGE_SHIFT; +} + +static u64 +nvkm_mem_addr(struct nvkm_memory *memory) +{ + struct nvkm_mem *mem = nvkm_mem(memory); + if (mem->pages == 1 && mem->mem) + return mem->dma[0]; + return ~0ULL; +} + +static u64 +nvkm_mem_size(struct nvkm_memory *memory) +{ + return nvkm_mem(memory)->pages << PAGE_SHIFT; +} + +static int +nvkm_mem_map_dma(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm, + struct nvkm_vma *vma, void *argv, u32 argc) +{ + struct nvkm_mem *mem = nvkm_mem(memory); + struct nvkm_vmm_map map = { + .memory = &mem->memory, + .offset = offset, + .dma = mem->dma, + }; + return nvkm_vmm_map(vmm, vma, argv, argc, &map); +} + +static void * +nvkm_mem_dtor(struct nvkm_memory *memory) +{ + struct nvkm_mem *mem = nvkm_mem(memory); + if (mem->mem) { + while (mem->pages--) { + dma_unmap_page(mem->mmu->subdev.device->dev, + mem->dma[mem->pages], PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(mem->mem[mem->pages]); + } + kvfree(mem->dma); + kvfree(mem->mem); + } + return mem; +} + +static const struct nvkm_memory_func +nvkm_mem_dma = { + .dtor = nvkm_mem_dtor, + .target = nvkm_mem_target, + .page = nvkm_mem_page, + .addr = nvkm_mem_addr, + .size = nvkm_mem_size, + .map = nvkm_mem_map_dma, +}; + +static int +nvkm_mem_map_sgl(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm, + struct nvkm_vma *vma, void *argv, u32 argc) +{ + struct nvkm_mem *mem = nvkm_mem(memory); + struct nvkm_vmm_map map = { + .memory = &mem->memory, + .offset = offset, + .sgl = mem->sgl, + }; + return nvkm_vmm_map(vmm, vma, argv, argc, &map); +} + +static const struct nvkm_memory_func +nvkm_mem_sgl = { + .dtor = nvkm_mem_dtor, + .target = nvkm_mem_target, + .page = nvkm_mem_page, + .addr = nvkm_mem_addr, + .size = nvkm_mem_size, + .map = nvkm_mem_map_sgl, +}; + +int +nvkm_mem_map_host(struct nvkm_memory *memory, void **pmap) +{ + struct nvkm_mem *mem = nvkm_mem(memory); + if (mem->mem) { + *pmap = vmap(mem->mem, mem->pages, VM_MAP, PAGE_KERNEL); + return *pmap ? 0 : -EFAULT; + } + return -EINVAL; +} + +static int +nvkm_mem_new_host(struct nvkm_mmu *mmu, int type, u8 page, u64 size, + void *argv, u32 argc, struct nvkm_memory **pmemory) +{ + struct device *dev = mmu->subdev.device->dev; + union { + struct nvif_mem_ram_vn vn; + struct nvif_mem_ram_v0 v0; + } *args = argv; + int ret = -ENOSYS; + enum nvkm_memory_target target; + struct nvkm_mem *mem; + gfp_t gfp = GFP_USER | __GFP_ZERO; + + if ( (mmu->type[type].type & NVKM_MEM_COHERENT) && + !(mmu->type[type].type & NVKM_MEM_UNCACHED)) + target = NVKM_MEM_TARGET_HOST; + else + target = NVKM_MEM_TARGET_NCOH; + + if (page != PAGE_SHIFT) + return -EINVAL; + + if (!(mem = kzalloc(sizeof(*mem), GFP_KERNEL))) + return -ENOMEM; + mem->target = target; + mem->mmu = mmu; + *pmemory = &mem->memory; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + if (args->v0.dma) { + nvkm_memory_ctor(&nvkm_mem_dma, &mem->memory); + mem->dma = args->v0.dma; + } else { + nvkm_memory_ctor(&nvkm_mem_sgl, &mem->memory); + mem->sgl = args->v0.sgl; + } + + if (!IS_ALIGNED(size, PAGE_SIZE)) + return -EINVAL; + mem->pages = size >> PAGE_SHIFT; + return 0; + } else + if ( (ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + kfree(mem); + return ret; + } + + nvkm_memory_ctor(&nvkm_mem_dma, &mem->memory); + size = ALIGN(size, PAGE_SIZE) >> PAGE_SHIFT; + + if (!(mem->mem = kvmalloc_array(size, sizeof(*mem->mem), GFP_KERNEL))) + return -ENOMEM; + if (!(mem->dma = kvmalloc_array(size, sizeof(*mem->dma), GFP_KERNEL))) + return -ENOMEM; + + if (mmu->dma_bits > 32) + gfp |= GFP_HIGHUSER; + else + gfp |= GFP_DMA32; + + for (mem->pages = 0; size; size--, mem->pages++) { + struct page *p = alloc_page(gfp); + if (!p) + return -ENOMEM; + + mem->dma[mem->pages] = dma_map_page(mmu->subdev.device->dev, + p, 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, mem->dma[mem->pages])) { + __free_page(p); + return -ENOMEM; + } + + mem->mem[mem->pages] = p; + } + + return 0; +} + +int +nvkm_mem_new_type(struct nvkm_mmu *mmu, int type, u8 page, u64 size, + void *argv, u32 argc, struct nvkm_memory **pmemory) +{ + struct nvkm_memory *memory = NULL; + int ret; + + if (mmu->type[type].type & NVKM_MEM_VRAM) { + ret = mmu->func->mem.vram(mmu, type, page, size, + argv, argc, &memory); + } else { + ret = nvkm_mem_new_host(mmu, type, page, size, + argv, argc, &memory); + } + + if (ret) + nvkm_memory_unref(&memory); + *pmemory = memory; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.h new file mode 100644 index 000000000..234267e1b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/mem.h @@ -0,0 +1,23 @@ +#ifndef __NVKM_MEM_H__ +#define __NVKM_MEM_H__ +#include "priv.h" + +int nvkm_mem_new_type(struct nvkm_mmu *, int type, u8 page, u64 size, + void *argv, u32 argc, struct nvkm_memory **); +int nvkm_mem_map_host(struct nvkm_memory *, void **pmap); + +int nv04_mem_new(struct nvkm_mmu *, int, u8, u64, void *, u32, + struct nvkm_memory **); +int nv04_mem_map(struct nvkm_mmu *, struct nvkm_memory *, void *, u32, + u64 *, u64 *, struct nvkm_vma **); + +int nv50_mem_new(struct nvkm_mmu *, int, u8, u64, void *, u32, + struct nvkm_memory **); +int nv50_mem_map(struct nvkm_mmu *, struct nvkm_memory *, void *, u32, + u64 *, u64 *, struct nvkm_vma **); + +int gf100_mem_new(struct nvkm_mmu *, int, u8, u64, void *, u32, + struct nvkm_memory **); +int gf100_mem_map(struct nvkm_mmu *, struct nvkm_memory *, void *, u32, + u64 *, u64 *, struct nvkm_vma **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memgf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memgf100.c new file mode 100644 index 000000000..d9c9bee45 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memgf100.c @@ -0,0 +1,94 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" + +#include +#include +#include + +#include +#include +#include +#include + +int +gf100_mem_map(struct nvkm_mmu *mmu, struct nvkm_memory *memory, void *argv, + u32 argc, u64 *paddr, u64 *psize, struct nvkm_vma **pvma) +{ + struct gf100_vmm_map_v0 uvmm = {}; + union { + struct gf100_mem_map_vn vn; + struct gf100_mem_map_v0 v0; + } *args = argv; + struct nvkm_device *device = mmu->subdev.device; + struct nvkm_vmm *bar = nvkm_bar_bar1_vmm(device); + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + uvmm.ro = args->v0.ro; + uvmm.kind = args->v0.kind; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + } else + return ret; + + ret = nvkm_vmm_get(bar, nvkm_memory_page(memory), + nvkm_memory_size(memory), pvma); + if (ret) + return ret; + + ret = nvkm_memory_map(memory, 0, bar, *pvma, &uvmm, sizeof(uvmm)); + if (ret) + return ret; + + *paddr = device->func->resource_addr(device, 1) + (*pvma)->addr; + *psize = (*pvma)->size; + return 0; +} + +int +gf100_mem_new(struct nvkm_mmu *mmu, int type, u8 page, u64 size, + void *argv, u32 argc, struct nvkm_memory **pmemory) +{ + union { + struct gf100_mem_vn vn; + struct gf100_mem_v0 v0; + } *args = argv; + int ret = -ENOSYS; + bool contig; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + contig = args->v0.contig; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + contig = false; + } else + return ret; + + if (mmu->type[type].type & (NVKM_MEM_DISP | NVKM_MEM_COMP)) + type = NVKM_RAM_MM_NORMAL; + else + type = NVKM_RAM_MM_MIXED; + + return nvkm_ram_get(mmu->subdev.device, type, 0x01, page, + size, contig, false, pmemory); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv04.c new file mode 100644 index 000000000..79a3b0cc9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv04.c @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" + +#include +#include + +#include +#include + +int +nv04_mem_map(struct nvkm_mmu *mmu, struct nvkm_memory *memory, void *argv, + u32 argc, u64 *paddr, u64 *psize, struct nvkm_vma **pvma) +{ + union { + struct nv04_mem_map_vn vn; + } *args = argv; + struct nvkm_device *device = mmu->subdev.device; + const u64 addr = nvkm_memory_addr(memory); + int ret = -ENOSYS; + + if ((ret = nvif_unvers(ret, &argv, &argc, args->vn))) + return ret; + + *paddr = device->func->resource_addr(device, 1) + addr; + *psize = nvkm_memory_size(memory); + *pvma = ERR_PTR(-ENODEV); + return 0; +} + +int +nv04_mem_new(struct nvkm_mmu *mmu, int type, u8 page, u64 size, + void *argv, u32 argc, struct nvkm_memory **pmemory) +{ + union { + struct nv04_mem_vn vn; + } *args = argv; + int ret = -ENOSYS; + + if ((ret = nvif_unvers(ret, &argv, &argc, args->vn))) + return ret; + + if (mmu->type[type].type & NVKM_MEM_MAPPABLE) + type = NVKM_RAM_MM_NORMAL; + else + type = NVKM_RAM_MM_NOMAP; + + return nvkm_ram_get(mmu->subdev.device, type, 0x01, page, + size, true, false, pmemory); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv50.c new file mode 100644 index 000000000..46759b89f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv50.c @@ -0,0 +1,88 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" + +#include +#include +#include + +#include +#include +#include +#include + +int +nv50_mem_map(struct nvkm_mmu *mmu, struct nvkm_memory *memory, void *argv, + u32 argc, u64 *paddr, u64 *psize, struct nvkm_vma **pvma) +{ + struct nv50_vmm_map_v0 uvmm = {}; + union { + struct nv50_mem_map_vn vn; + struct nv50_mem_map_v0 v0; + } *args = argv; + struct nvkm_device *device = mmu->subdev.device; + struct nvkm_vmm *bar = nvkm_bar_bar1_vmm(device); + u64 size = nvkm_memory_size(memory); + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + uvmm.ro = args->v0.ro; + uvmm.kind = args->v0.kind; + uvmm.comp = args->v0.comp; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + } else + return ret; + + ret = nvkm_vmm_get(bar, 12, size, pvma); + if (ret) + return ret; + + *paddr = device->func->resource_addr(device, 1) + (*pvma)->addr; + *psize = (*pvma)->size; + return nvkm_memory_map(memory, 0, bar, *pvma, &uvmm, sizeof(uvmm)); +} + +int +nv50_mem_new(struct nvkm_mmu *mmu, int type, u8 page, u64 size, + void *argv, u32 argc, struct nvkm_memory **pmemory) +{ + union { + struct nv50_mem_vn vn; + struct nv50_mem_v0 v0; + } *args = argv; + int ret = -ENOSYS; + bool contig; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + type = args->v0.bankswz ? 0x02 : 0x01; + contig = args->v0.contig; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + type = 0x01; + contig = false; + } else + return -ENOSYS; + + return nvkm_ram_get(mmu->subdev.device, NVKM_RAM_MM_NORMAL, type, + page, size, contig, false, pmemory); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c new file mode 100644 index 000000000..0674aa8f6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "mem.h" +#include "vmm.h" + +#include + +const struct nvkm_mmu_func +nv04_mmu = { + .dma_bits = 32, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_NV04}}, + .mem = {{ -1, -1, NVIF_CLASS_MEM_NV04}, nv04_mem_new, nv04_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_NV04}, nv04_vmm_new, true }, +}; + +int +nv04_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&nv04_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c new file mode 100644 index 000000000..909f92b72 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c @@ -0,0 +1,58 @@ +/* + * 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 "mem.h" +#include "vmm.h" + +#include + +#include + +static void +nv41_mmu_init(struct nvkm_mmu *mmu) +{ + struct nvkm_device *device = mmu->subdev.device; + nvkm_wr32(device, 0x100800, 0x00000002 | mmu->vmm->pd->pt[0]->addr); + nvkm_mask(device, 0x10008c, 0x00000100, 0x00000100); + nvkm_wr32(device, 0x100820, 0x00000000); +} + +static const struct nvkm_mmu_func +nv41_mmu = { + .init = nv41_mmu_init, + .dma_bits = 39, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_NV04}}, + .mem = {{ -1, -1, NVIF_CLASS_MEM_NV04}, nv04_mem_new, nv04_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_NV04}, nv41_vmm_new, true }, +}; + +int +nv41_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + if (device->type == NVKM_DEVICE_AGP || + !nvkm_boolopt(device->cfgopt, "NvPCIE", true)) + return nv04_mmu_new(device, type, inst, pmmu); + + return nvkm_mmu_new_(&nv41_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c new file mode 100644 index 000000000..dd2a8d461 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c @@ -0,0 +1,73 @@ +/* + * 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 "mem.h" +#include "vmm.h" + +#include + +#include + +static void +nv44_mmu_init(struct nvkm_mmu *mmu) +{ + struct nvkm_device *device = mmu->subdev.device; + struct nvkm_memory *pt = mmu->vmm->pd->pt[0]->memory; + u32 addr; + + /* calculate vram address of this PRAMIN block, object must be + * allocated on 512KiB alignment, and not exceed a total size + * of 512KiB for this to work correctly + */ + addr = nvkm_rd32(device, 0x10020c); + addr -= ((nvkm_memory_addr(pt) >> 19) + 1) << 19; + + nvkm_wr32(device, 0x100850, 0x80000000); + nvkm_wr32(device, 0x100818, mmu->vmm->null); + nvkm_wr32(device, 0x100804, (nvkm_memory_size(pt) / 4) * 4096); + nvkm_wr32(device, 0x100850, 0x00008000); + nvkm_mask(device, 0x10008c, 0x00000200, 0x00000200); + nvkm_wr32(device, 0x100820, 0x00000000); + nvkm_wr32(device, 0x10082c, 0x00000001); + nvkm_wr32(device, 0x100800, addr | 0x00000010); +} + +static const struct nvkm_mmu_func +nv44_mmu = { + .init = nv44_mmu_init, + .dma_bits = 39, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_NV04}}, + .mem = {{ -1, -1, NVIF_CLASS_MEM_NV04}, nv04_mem_new, nv04_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_NV04}, nv44_vmm_new, true }, +}; + +int +nv44_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + if (device->type == NVKM_DEVICE_AGP || + !nvkm_boolopt(device->cfgopt, "NvPCIE", true)) + return nv04_mmu_new(device, type, inst, pmmu); + + return nvkm_mmu_new_(&nv44_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c new file mode 100644 index 000000000..78d46e35d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c @@ -0,0 +1,78 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "mem.h" +#include "vmm.h" + +#include + +const u8 * +nv50_mmu_kind(struct nvkm_mmu *base, int *count, u8 *invalid) +{ + /* 0x01: no bank swizzle + * 0x02: bank swizzled + * 0x7f: invalid + * + * 0x01/0x02 are values understood by the VRAM allocator, + * and are required to avoid mixing the two types within + * a certain range. + */ + static const u8 + kind[128] = { + 0x01, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, /* 0x00 */ + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x01, 0x01, 0x01, 0x01, 0x7f, 0x7f, 0x7f, 0x7f, /* 0x10 */ + 0x02, 0x02, 0x02, 0x02, 0x7f, 0x7f, 0x7f, 0x7f, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, /* 0x20 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, /* 0x30 */ + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, /* 0x40 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x01, 0x01, 0x7f, /* 0x50 */ + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, /* 0x60 */ + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x7f, 0x02, 0x7f, 0x01, 0x7f, 0x02, 0x7f, /* 0x70 */ + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x7f, 0x7f + }; + *count = ARRAY_SIZE(kind); + *invalid = 0x7f; + return kind; +} + +static const struct nvkm_mmu_func +nv50_mmu = { + .dma_bits = 40, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_NV50}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_NV50}, nv50_mem_new, nv50_mem_map }, + .vmm = {{ -1, -1, NVIF_CLASS_VMM_NV50}, nv50_vmm_new, false, 0x1400 }, + .kind = nv50_mmu_kind, +}; + +int +nv50_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&nv50_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h new file mode 100644 index 000000000..5265bf4d8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_MMU_PRIV_H__ +#define __NVKM_MMU_PRIV_H__ +#define nvkm_mmu(p) container_of((p), struct nvkm_mmu, subdev) +#include + +void nvkm_mmu_ctor(const struct nvkm_mmu_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_mmu *); +int nvkm_mmu_new_(const struct nvkm_mmu_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_mmu **); + +struct nvkm_mmu_func { + void (*init)(struct nvkm_mmu *); + + u8 dma_bits; + + struct { + struct nvkm_sclass user; + } mmu; + + struct { + struct nvkm_sclass user; + int (*vram)(struct nvkm_mmu *, int type, u8 page, u64 size, + void *argv, u32 argc, struct nvkm_memory **); + int (*umap)(struct nvkm_mmu *, struct nvkm_memory *, void *argv, + u32 argc, u64 *addr, u64 *size, struct nvkm_vma **); + } mem; + + struct { + struct nvkm_sclass user; + int (*ctor)(struct nvkm_mmu *, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *, + const char *name, struct nvkm_vmm **); + bool global; + u32 pd_offset; + } vmm; + + const u8 *(*kind)(struct nvkm_mmu *, int *count, u8 *invalid); + bool kind_sys; +}; + +extern const struct nvkm_mmu_func nv04_mmu; + +const u8 *nv50_mmu_kind(struct nvkm_mmu *, int *count, u8 *invalid); + +const u8 *gf100_mmu_kind(struct nvkm_mmu *, int *count, u8 *invalid); + +const u8 *gm200_mmu_kind(struct nvkm_mmu *, int *, u8 *); + +struct nvkm_mmu_pt { + union { + struct nvkm_mmu_ptc *ptc; + struct nvkm_mmu_ptp *ptp; + }; + struct nvkm_memory *memory; + bool sub; + u16 base; + u64 addr; + struct list_head head; +}; + +void nvkm_mmu_ptc_dump(struct nvkm_mmu *); +struct nvkm_mmu_pt * +nvkm_mmu_ptc_get(struct nvkm_mmu *, u32 size, u32 align, bool zero); +void nvkm_mmu_ptc_put(struct nvkm_mmu *, bool force, struct nvkm_mmu_pt **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/tu102.c new file mode 100644 index 000000000..8d060ce47 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/tu102.c @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Red Hat Inc. + * Copyright 2019 NVIDIA Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "mem.h" +#include "vmm.h" + +#include + +#include + +static const u8 * +tu102_mmu_kind(struct nvkm_mmu *mmu, int *count, u8 *invalid) +{ + static const u8 + kind[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00 */ + 0x06, 0x06, 0x02, 0x01, 0x03, 0x04, 0x05, 0x07, + }; + *count = ARRAY_SIZE(kind); + *invalid = 0x07; + return kind; +} + +static const struct nvkm_mmu_func +tu102_mmu = { + .dma_bits = 47, + .mmu = {{ -1, -1, NVIF_CLASS_MMU_GF100}}, + .mem = {{ -1, 0, NVIF_CLASS_MEM_GF100}, gf100_mem_new, gf100_mem_map }, + .vmm = {{ -1, 0, NVIF_CLASS_VMM_GP100}, tu102_vmm_new }, + .kind = tu102_mmu_kind, + .kind_sys = true, +}; + +int +tu102_mmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mmu **pmmu) +{ + return nvkm_mmu_new_(&tu102_mmu, device, type, inst, pmmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.c new file mode 100644 index 000000000..e530bb8b3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.c @@ -0,0 +1,190 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "umem.h" +#include "ummu.h" + +#include +#include +#include + +#include +#include +#include + +static const struct nvkm_object_func nvkm_umem; +struct nvkm_memory * +nvkm_umem_search(struct nvkm_client *client, u64 handle) +{ + struct nvkm_client *master = client->object.client; + struct nvkm_memory *memory = NULL; + struct nvkm_object *object; + struct nvkm_umem *umem; + + object = nvkm_object_search(client, handle, &nvkm_umem); + if (IS_ERR(object)) { + if (client != master) { + spin_lock(&master->lock); + list_for_each_entry(umem, &master->umem, head) { + if (umem->object.object == handle) { + memory = nvkm_memory_ref(umem->memory); + break; + } + } + spin_unlock(&master->lock); + } + } else { + umem = nvkm_umem(object); + memory = nvkm_memory_ref(umem->memory); + } + + return memory ? memory : ERR_PTR(-ENOENT); +} + +static int +nvkm_umem_unmap(struct nvkm_object *object) +{ + struct nvkm_umem *umem = nvkm_umem(object); + + if (!umem->map) + return -EEXIST; + + if (umem->io) { + if (!IS_ERR(umem->bar)) { + struct nvkm_device *device = umem->mmu->subdev.device; + nvkm_vmm_put(nvkm_bar_bar1_vmm(device), &umem->bar); + } else { + umem->bar = NULL; + } + } else { + vunmap(umem->map); + umem->map = NULL; + } + + return 0; +} + +static int +nvkm_umem_map(struct nvkm_object *object, void *argv, u32 argc, + enum nvkm_object_map *type, u64 *handle, u64 *length) +{ + struct nvkm_umem *umem = nvkm_umem(object); + struct nvkm_mmu *mmu = umem->mmu; + + if (!umem->mappable) + return -EINVAL; + if (umem->map) + return -EEXIST; + + if ((umem->type & NVKM_MEM_HOST) && !argc) { + int ret = nvkm_mem_map_host(umem->memory, &umem->map); + if (ret) + return ret; + + *handle = (unsigned long)(void *)umem->map; + *length = nvkm_memory_size(umem->memory); + *type = NVKM_OBJECT_MAP_VA; + return 0; + } else + if ((umem->type & NVKM_MEM_VRAM) || + (umem->type & NVKM_MEM_KIND)) { + int ret = mmu->func->mem.umap(mmu, umem->memory, argv, argc, + handle, length, &umem->bar); + if (ret) + return ret; + + *type = NVKM_OBJECT_MAP_IO; + } else { + return -EINVAL; + } + + umem->io = (*type == NVKM_OBJECT_MAP_IO); + return 0; +} + +static void * +nvkm_umem_dtor(struct nvkm_object *object) +{ + struct nvkm_umem *umem = nvkm_umem(object); + spin_lock(&umem->object.client->lock); + list_del_init(&umem->head); + spin_unlock(&umem->object.client->lock); + nvkm_memory_unref(&umem->memory); + return umem; +} + +static const struct nvkm_object_func +nvkm_umem = { + .dtor = nvkm_umem_dtor, + .map = nvkm_umem_map, + .unmap = nvkm_umem_unmap, +}; + +int +nvkm_umem_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + struct nvkm_mmu *mmu = nvkm_ummu(oclass->parent)->mmu; + union { + struct nvif_mem_v0 v0; + } *args = argv; + struct nvkm_umem *umem; + int type, ret = -ENOSYS; + u8 page; + u64 size; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, true))) { + type = args->v0.type; + page = args->v0.page; + size = args->v0.size; + } else + return ret; + + if (type >= mmu->type_nr) + return -EINVAL; + + if (!(umem = kzalloc(sizeof(*umem), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nvkm_umem, oclass, &umem->object); + umem->mmu = mmu; + umem->type = mmu->type[type].type; + INIT_LIST_HEAD(&umem->head); + *pobject = &umem->object; + + if (mmu->type[type].type & NVKM_MEM_MAPPABLE) { + page = max_t(u8, page, PAGE_SHIFT); + umem->mappable = true; + } + + ret = nvkm_mem_new_type(mmu, type, page, size, argv, argc, + &umem->memory); + if (ret) + return ret; + + spin_lock(&umem->object.client->lock); + list_add(&umem->head, &umem->object.client->umem); + spin_unlock(&umem->object.client->lock); + + args->v0.page = nvkm_memory_page(umem->memory); + args->v0.addr = nvkm_memory_addr(umem->memory); + args->v0.size = nvkm_memory_size(umem->memory); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.h new file mode 100644 index 000000000..d56a59401 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/umem.h @@ -0,0 +1,25 @@ +#ifndef __NVKM_UMEM_H__ +#define __NVKM_UMEM_H__ +#define nvkm_umem(p) container_of((p), struct nvkm_umem, object) +#include +#include "mem.h" + +struct nvkm_umem { + struct nvkm_object object; + struct nvkm_mmu *mmu; + u8 type:8; + bool mappable:1; + bool io:1; + + struct nvkm_memory *memory; + struct list_head head; + + union { + struct nvkm_vma *bar; + void *map; + }; +}; + +int nvkm_umem_new(const struct nvkm_oclass *, void *argv, u32 argc, + struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.c new file mode 100644 index 000000000..6870fda4b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.c @@ -0,0 +1,181 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ummu.h" +#include "umem.h" +#include "uvmm.h" + +#include + +#include +#include + +static int +nvkm_ummu_sclass(struct nvkm_object *object, int index, + struct nvkm_oclass *oclass) +{ + struct nvkm_mmu *mmu = nvkm_ummu(object)->mmu; + + if (mmu->func->mem.user.oclass) { + if (index-- == 0) { + oclass->base = mmu->func->mem.user; + oclass->ctor = nvkm_umem_new; + return 0; + } + } + + if (mmu->func->vmm.user.oclass) { + if (index-- == 0) { + oclass->base = mmu->func->vmm.user; + oclass->ctor = nvkm_uvmm_new; + return 0; + } + } + + return -EINVAL; +} + +static int +nvkm_ummu_heap(struct nvkm_ummu *ummu, void *argv, u32 argc) +{ + struct nvkm_mmu *mmu = ummu->mmu; + union { + struct nvif_mmu_heap_v0 v0; + } *args = argv; + int ret = -ENOSYS; + u8 index; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + if ((index = args->v0.index) >= mmu->heap_nr) + return -EINVAL; + args->v0.size = mmu->heap[index].size; + } else + return ret; + + return 0; +} + +static int +nvkm_ummu_type(struct nvkm_ummu *ummu, void *argv, u32 argc) +{ + struct nvkm_mmu *mmu = ummu->mmu; + union { + struct nvif_mmu_type_v0 v0; + } *args = argv; + int ret = -ENOSYS; + u8 type, index; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + if ((index = args->v0.index) >= mmu->type_nr) + return -EINVAL; + type = mmu->type[index].type; + args->v0.heap = mmu->type[index].heap; + args->v0.vram = !!(type & NVKM_MEM_VRAM); + args->v0.host = !!(type & NVKM_MEM_HOST); + args->v0.comp = !!(type & NVKM_MEM_COMP); + args->v0.disp = !!(type & NVKM_MEM_DISP); + args->v0.kind = !!(type & NVKM_MEM_KIND); + args->v0.mappable = !!(type & NVKM_MEM_MAPPABLE); + args->v0.coherent = !!(type & NVKM_MEM_COHERENT); + args->v0.uncached = !!(type & NVKM_MEM_UNCACHED); + } else + return ret; + + return 0; +} + +static int +nvkm_ummu_kind(struct nvkm_ummu *ummu, void *argv, u32 argc) +{ + struct nvkm_mmu *mmu = ummu->mmu; + union { + struct nvif_mmu_kind_v0 v0; + } *args = argv; + const u8 *kind = NULL; + int ret = -ENOSYS, count = 0; + u8 kind_inv = 0; + + if (mmu->func->kind) + kind = mmu->func->kind(mmu, &count, &kind_inv); + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, true))) { + if (argc != args->v0.count * sizeof(*args->v0.data)) + return -EINVAL; + if (args->v0.count > count) + return -EINVAL; + args->v0.kind_inv = kind_inv; + memcpy(args->v0.data, kind, args->v0.count); + } else + return ret; + + return 0; +} + +static int +nvkm_ummu_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) +{ + struct nvkm_ummu *ummu = nvkm_ummu(object); + switch (mthd) { + case NVIF_MMU_V0_HEAP: return nvkm_ummu_heap(ummu, argv, argc); + case NVIF_MMU_V0_TYPE: return nvkm_ummu_type(ummu, argv, argc); + case NVIF_MMU_V0_KIND: return nvkm_ummu_kind(ummu, argv, argc); + default: + break; + } + return -EINVAL; +} + +static const struct nvkm_object_func +nvkm_ummu = { + .mthd = nvkm_ummu_mthd, + .sclass = nvkm_ummu_sclass, +}; + +int +nvkm_ummu_new(struct nvkm_device *device, const struct nvkm_oclass *oclass, + void *argv, u32 argc, struct nvkm_object **pobject) +{ + union { + struct nvif_mmu_v0 v0; + } *args = argv; + struct nvkm_mmu *mmu = device->mmu; + struct nvkm_ummu *ummu; + int ret = -ENOSYS, kinds = 0; + u8 unused = 0; + + if (mmu->func->kind) + mmu->func->kind(mmu, &kinds, &unused); + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + args->v0.dmabits = mmu->dma_bits; + args->v0.heap_nr = mmu->heap_nr; + args->v0.type_nr = mmu->type_nr; + args->v0.kind_nr = kinds; + } else + return ret; + + if (!(ummu = kzalloc(sizeof(*ummu), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nvkm_ummu, oclass, &ummu->object); + ummu->mmu = mmu; + *pobject = &ummu->object; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.h new file mode 100644 index 000000000..0cd510dcf --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/ummu.h @@ -0,0 +1,14 @@ +#ifndef __NVKM_UMMU_H__ +#define __NVKM_UMMU_H__ +#define nvkm_ummu(p) container_of((p), struct nvkm_ummu, object) +#include +#include "priv.h" + +struct nvkm_ummu { + struct nvkm_object object; + struct nvkm_mmu *mmu; +}; + +int nvkm_ummu_new(struct nvkm_device *, const struct nvkm_oclass *, + void *argv, u32 argc, struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c new file mode 100644 index 000000000..186b4e63e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c @@ -0,0 +1,404 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "uvmm.h" +#include "umem.h" +#include "ummu.h" + +#include +#include + +#include +#include + +static const struct nvkm_object_func nvkm_uvmm; +struct nvkm_vmm * +nvkm_uvmm_search(struct nvkm_client *client, u64 handle) +{ + struct nvkm_object *object; + + object = nvkm_object_search(client, handle, &nvkm_uvmm); + if (IS_ERR(object)) + return (void *)object; + + return nvkm_uvmm(object)->vmm; +} + +static int +nvkm_uvmm_mthd_pfnclr(struct nvkm_uvmm *uvmm, void *argv, u32 argc) +{ + union { + struct nvif_vmm_pfnclr_v0 v0; + } *args = argv; + struct nvkm_vmm *vmm = uvmm->vmm; + int ret = -ENOSYS; + u64 addr, size; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + addr = args->v0.addr; + size = args->v0.size; + } else + return ret; + + if (size) { + mutex_lock(&vmm->mutex); + ret = nvkm_vmm_pfn_unmap(vmm, addr, size); + mutex_unlock(&vmm->mutex); + } + + return ret; +} + +static int +nvkm_uvmm_mthd_pfnmap(struct nvkm_uvmm *uvmm, void *argv, u32 argc) +{ + union { + struct nvif_vmm_pfnmap_v0 v0; + } *args = argv; + struct nvkm_vmm *vmm = uvmm->vmm; + int ret = -ENOSYS; + u64 addr, size, *phys; + u8 page; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, true))) { + page = args->v0.page; + addr = args->v0.addr; + size = args->v0.size; + phys = args->v0.phys; + if (argc != (size >> page) * sizeof(args->v0.phys[0])) + return -EINVAL; + } else + return ret; + + if (size) { + mutex_lock(&vmm->mutex); + ret = nvkm_vmm_pfn_map(vmm, page, addr, size, phys); + mutex_unlock(&vmm->mutex); + } + + return ret; +} + +static int +nvkm_uvmm_mthd_unmap(struct nvkm_uvmm *uvmm, void *argv, u32 argc) +{ + union { + struct nvif_vmm_unmap_v0 v0; + } *args = argv; + struct nvkm_vmm *vmm = uvmm->vmm; + struct nvkm_vma *vma; + int ret = -ENOSYS; + u64 addr; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + addr = args->v0.addr; + } else + return ret; + + mutex_lock(&vmm->mutex); + vma = nvkm_vmm_node_search(vmm, addr); + if (ret = -ENOENT, !vma || vma->addr != addr) { + VMM_DEBUG(vmm, "lookup %016llx: %016llx", + addr, vma ? vma->addr : ~0ULL); + goto done; + } + + if (ret = -ENOENT, vma->busy) { + VMM_DEBUG(vmm, "denied %016llx: %d", addr, vma->busy); + goto done; + } + + if (ret = -EINVAL, !vma->memory) { + VMM_DEBUG(vmm, "unmapped"); + goto done; + } + + nvkm_vmm_unmap_locked(vmm, vma, false); + ret = 0; +done: + mutex_unlock(&vmm->mutex); + return ret; +} + +static int +nvkm_uvmm_mthd_map(struct nvkm_uvmm *uvmm, void *argv, u32 argc) +{ + struct nvkm_client *client = uvmm->object.client; + union { + struct nvif_vmm_map_v0 v0; + } *args = argv; + u64 addr, size, handle, offset; + struct nvkm_vmm *vmm = uvmm->vmm; + struct nvkm_vma *vma; + struct nvkm_memory *memory; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, true))) { + addr = args->v0.addr; + size = args->v0.size; + handle = args->v0.memory; + offset = args->v0.offset; + } else + return ret; + + memory = nvkm_umem_search(client, handle); + if (IS_ERR(memory)) { + VMM_DEBUG(vmm, "memory %016llx %ld\n", handle, PTR_ERR(memory)); + return PTR_ERR(memory); + } + + mutex_lock(&vmm->mutex); + if (ret = -ENOENT, !(vma = nvkm_vmm_node_search(vmm, addr))) { + VMM_DEBUG(vmm, "lookup %016llx", addr); + goto fail; + } + + if (ret = -ENOENT, vma->busy) { + VMM_DEBUG(vmm, "denied %016llx: %d", addr, vma->busy); + goto fail; + } + + if (ret = -EINVAL, vma->mapped && !vma->memory) { + VMM_DEBUG(vmm, "pfnmap %016llx", addr); + goto fail; + } + + if (ret = -EINVAL, vma->addr != addr || vma->size != size) { + if (addr + size > vma->addr + vma->size || vma->memory || + (vma->refd == NVKM_VMA_PAGE_NONE && !vma->mapref)) { + VMM_DEBUG(vmm, "split %d %d %d " + "%016llx %016llx %016llx %016llx", + !!vma->memory, vma->refd, vma->mapref, + addr, size, vma->addr, (u64)vma->size); + goto fail; + } + + vma = nvkm_vmm_node_split(vmm, vma, addr, size); + if (!vma) { + ret = -ENOMEM; + goto fail; + } + } + vma->busy = true; + mutex_unlock(&vmm->mutex); + + ret = nvkm_memory_map(memory, offset, vmm, vma, argv, argc); + if (ret == 0) { + /* Successful map will clear vma->busy. */ + nvkm_memory_unref(&memory); + return 0; + } + + mutex_lock(&vmm->mutex); + vma->busy = false; + nvkm_vmm_unmap_region(vmm, vma); +fail: + mutex_unlock(&vmm->mutex); + nvkm_memory_unref(&memory); + return ret; +} + +static int +nvkm_uvmm_mthd_put(struct nvkm_uvmm *uvmm, void *argv, u32 argc) +{ + union { + struct nvif_vmm_put_v0 v0; + } *args = argv; + struct nvkm_vmm *vmm = uvmm->vmm; + struct nvkm_vma *vma; + int ret = -ENOSYS; + u64 addr; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + addr = args->v0.addr; + } else + return ret; + + mutex_lock(&vmm->mutex); + vma = nvkm_vmm_node_search(vmm, args->v0.addr); + if (ret = -ENOENT, !vma || vma->addr != addr || vma->part) { + VMM_DEBUG(vmm, "lookup %016llx: %016llx %d", addr, + vma ? vma->addr : ~0ULL, vma ? vma->part : 0); + goto done; + } + + if (ret = -ENOENT, vma->busy) { + VMM_DEBUG(vmm, "denied %016llx: %d", addr, vma->busy); + goto done; + } + + nvkm_vmm_put_locked(vmm, vma); + ret = 0; +done: + mutex_unlock(&vmm->mutex); + return ret; +} + +static int +nvkm_uvmm_mthd_get(struct nvkm_uvmm *uvmm, void *argv, u32 argc) +{ + union { + struct nvif_vmm_get_v0 v0; + } *args = argv; + struct nvkm_vmm *vmm = uvmm->vmm; + struct nvkm_vma *vma; + int ret = -ENOSYS; + bool getref, mapref, sparse; + u8 page, align; + u64 size; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + getref = args->v0.type == NVIF_VMM_GET_V0_PTES; + mapref = args->v0.type == NVIF_VMM_GET_V0_ADDR; + sparse = args->v0.sparse; + page = args->v0.page; + align = args->v0.align; + size = args->v0.size; + } else + return ret; + + mutex_lock(&vmm->mutex); + ret = nvkm_vmm_get_locked(vmm, getref, mapref, sparse, + page, align, size, &vma); + mutex_unlock(&vmm->mutex); + if (ret) + return ret; + + args->v0.addr = vma->addr; + return ret; +} + +static int +nvkm_uvmm_mthd_page(struct nvkm_uvmm *uvmm, void *argv, u32 argc) +{ + union { + struct nvif_vmm_page_v0 v0; + } *args = argv; + const struct nvkm_vmm_page *page; + int ret = -ENOSYS; + u8 type, index, nr; + + page = uvmm->vmm->func->page; + for (nr = 0; page[nr].shift; nr++); + + if (!(nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + if ((index = args->v0.index) >= nr) + return -EINVAL; + type = page[index].type; + args->v0.shift = page[index].shift; + args->v0.sparse = !!(type & NVKM_VMM_PAGE_SPARSE); + args->v0.vram = !!(type & NVKM_VMM_PAGE_VRAM); + args->v0.host = !!(type & NVKM_VMM_PAGE_HOST); + args->v0.comp = !!(type & NVKM_VMM_PAGE_COMP); + } else + return -ENOSYS; + + return 0; +} + +static int +nvkm_uvmm_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) +{ + struct nvkm_uvmm *uvmm = nvkm_uvmm(object); + switch (mthd) { + case NVIF_VMM_V0_PAGE : return nvkm_uvmm_mthd_page (uvmm, argv, argc); + case NVIF_VMM_V0_GET : return nvkm_uvmm_mthd_get (uvmm, argv, argc); + case NVIF_VMM_V0_PUT : return nvkm_uvmm_mthd_put (uvmm, argv, argc); + case NVIF_VMM_V0_MAP : return nvkm_uvmm_mthd_map (uvmm, argv, argc); + case NVIF_VMM_V0_UNMAP : return nvkm_uvmm_mthd_unmap (uvmm, argv, argc); + case NVIF_VMM_V0_PFNMAP: return nvkm_uvmm_mthd_pfnmap(uvmm, argv, argc); + case NVIF_VMM_V0_PFNCLR: return nvkm_uvmm_mthd_pfnclr(uvmm, argv, argc); + case NVIF_VMM_V0_MTHD(0x00) ... NVIF_VMM_V0_MTHD(0x7f): + if (uvmm->vmm->func->mthd) { + return uvmm->vmm->func->mthd(uvmm->vmm, + uvmm->object.client, + mthd, argv, argc); + } + break; + default: + break; + } + return -EINVAL; +} + +static void * +nvkm_uvmm_dtor(struct nvkm_object *object) +{ + struct nvkm_uvmm *uvmm = nvkm_uvmm(object); + nvkm_vmm_unref(&uvmm->vmm); + return uvmm; +} + +static const struct nvkm_object_func +nvkm_uvmm = { + .dtor = nvkm_uvmm_dtor, + .mthd = nvkm_uvmm_mthd, +}; + +int +nvkm_uvmm_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + struct nvkm_mmu *mmu = nvkm_ummu(oclass->parent)->mmu; + const bool more = oclass->base.maxver >= 0; + union { + struct nvif_vmm_v0 v0; + } *args = argv; + const struct nvkm_vmm_page *page; + struct nvkm_uvmm *uvmm; + int ret = -ENOSYS; + u64 addr, size; + bool managed; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, more))) { + managed = args->v0.managed != 0; + addr = args->v0.addr; + size = args->v0.size; + } else + return ret; + + if (!(uvmm = kzalloc(sizeof(*uvmm), GFP_KERNEL))) + return -ENOMEM; + nvkm_object_ctor(&nvkm_uvmm, oclass, &uvmm->object); + *pobject = &uvmm->object; + + if (!mmu->vmm) { + ret = mmu->func->vmm.ctor(mmu, managed, addr, size, argv, argc, + NULL, "user", &uvmm->vmm); + if (ret) + return ret; + + uvmm->vmm->debug = max(uvmm->vmm->debug, oclass->client->debug); + } else { + if (size) + return -EINVAL; + + uvmm->vmm = nvkm_vmm_ref(mmu->vmm); + } + + page = uvmm->vmm->func->page; + args->v0.page_nr = 0; + while (page && (page++)->shift) + args->v0.page_nr++; + args->v0.addr = uvmm->vmm->start; + args->v0.size = uvmm->vmm->limit; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.h new file mode 100644 index 000000000..71dab55e1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.h @@ -0,0 +1,14 @@ +#ifndef __NVKM_UVMM_H__ +#define __NVKM_UVMM_H__ +#define nvkm_uvmm(p) container_of((p), struct nvkm_uvmm, object) +#include +#include "vmm.h" + +struct nvkm_uvmm { + struct nvkm_object object; + struct nvkm_vmm *vmm; +}; + +int nvkm_uvmm_new(const struct nvkm_oclass *, void *argv, u32 argc, + struct nvkm_object **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c new file mode 100644 index 000000000..ae793f400 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -0,0 +1,1869 @@ +/* + * Copyright 2017 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. + */ +#define NVKM_VMM_LEVELS_MAX 5 +#include "vmm.h" + +#include + +static void +nvkm_vmm_pt_del(struct nvkm_vmm_pt **ppgt) +{ + struct nvkm_vmm_pt *pgt = *ppgt; + if (pgt) { + kvfree(pgt->pde); + kfree(pgt); + *ppgt = NULL; + } +} + + +static struct nvkm_vmm_pt * +nvkm_vmm_pt_new(const struct nvkm_vmm_desc *desc, bool sparse, + const struct nvkm_vmm_page *page) +{ + const u32 pten = 1 << desc->bits; + struct nvkm_vmm_pt *pgt; + u32 lpte = 0; + + if (desc->type > PGT) { + if (desc->type == SPT) { + const struct nvkm_vmm_desc *pair = page[-1].desc; + lpte = pten >> (desc->bits - pair->bits); + } else { + lpte = pten; + } + } + + if (!(pgt = kzalloc(sizeof(*pgt) + lpte, GFP_KERNEL))) + return NULL; + pgt->page = page ? page->shift : 0; + pgt->sparse = sparse; + + if (desc->type == PGD) { + pgt->pde = kvcalloc(pten, sizeof(*pgt->pde), GFP_KERNEL); + if (!pgt->pde) { + kfree(pgt); + return NULL; + } + } + + return pgt; +} + +struct nvkm_vmm_iter { + const struct nvkm_vmm_page *page; + const struct nvkm_vmm_desc *desc; + struct nvkm_vmm *vmm; + u64 cnt; + u16 max, lvl; + u32 pte[NVKM_VMM_LEVELS_MAX]; + struct nvkm_vmm_pt *pt[NVKM_VMM_LEVELS_MAX]; + int flush; +}; + +#ifdef CONFIG_NOUVEAU_DEBUG_MMU +static const char * +nvkm_vmm_desc_type(const struct nvkm_vmm_desc *desc) +{ + switch (desc->type) { + case PGD: return "PGD"; + case PGT: return "PGT"; + case SPT: return "SPT"; + case LPT: return "LPT"; + default: + return "UNKNOWN"; + } +} + +static void +nvkm_vmm_trace(struct nvkm_vmm_iter *it, char *buf) +{ + int lvl; + for (lvl = it->max; lvl >= 0; lvl--) { + if (lvl >= it->lvl) + buf += sprintf(buf, "%05x:", it->pte[lvl]); + else + buf += sprintf(buf, "xxxxx:"); + } +} + +#define TRA(i,f,a...) do { \ + char _buf[NVKM_VMM_LEVELS_MAX * 7]; \ + struct nvkm_vmm_iter *_it = (i); \ + nvkm_vmm_trace(_it, _buf); \ + VMM_TRACE(_it->vmm, "%s "f, _buf, ##a); \ +} while(0) +#else +#define TRA(i,f,a...) +#endif + +static inline void +nvkm_vmm_flush_mark(struct nvkm_vmm_iter *it) +{ + it->flush = min(it->flush, it->max - it->lvl); +} + +static inline void +nvkm_vmm_flush(struct nvkm_vmm_iter *it) +{ + if (it->flush != NVKM_VMM_LEVELS_MAX) { + if (it->vmm->func->flush) { + TRA(it, "flush: %d", it->flush); + it->vmm->func->flush(it->vmm, it->flush); + } + it->flush = NVKM_VMM_LEVELS_MAX; + } +} + +static void +nvkm_vmm_unref_pdes(struct nvkm_vmm_iter *it) +{ + const struct nvkm_vmm_desc *desc = it->desc; + const int type = desc[it->lvl].type == SPT; + struct nvkm_vmm_pt *pgd = it->pt[it->lvl + 1]; + struct nvkm_vmm_pt *pgt = it->pt[it->lvl]; + struct nvkm_mmu_pt *pt = pgt->pt[type]; + struct nvkm_vmm *vmm = it->vmm; + u32 pdei = it->pte[it->lvl + 1]; + + /* Recurse up the tree, unreferencing/destroying unneeded PDs. */ + it->lvl++; + if (--pgd->refs[0]) { + const struct nvkm_vmm_desc_func *func = desc[it->lvl].func; + /* PD has other valid PDEs, so we need a proper update. */ + TRA(it, "PDE unmap %s", nvkm_vmm_desc_type(&desc[it->lvl - 1])); + pgt->pt[type] = NULL; + if (!pgt->refs[!type]) { + /* PDE no longer required. */ + if (pgd->pt[0]) { + if (pgt->sparse) { + func->sparse(vmm, pgd->pt[0], pdei, 1); + pgd->pde[pdei] = NVKM_VMM_PDE_SPARSE; + } else { + func->unmap(vmm, pgd->pt[0], pdei, 1); + pgd->pde[pdei] = NULL; + } + } else { + /* Special handling for Tesla-class GPUs, + * where there's no central PD, but each + * instance has its own embedded PD. + */ + func->pde(vmm, pgd, pdei); + pgd->pde[pdei] = NULL; + } + } else { + /* PDE was pointing at dual-PTs and we're removing + * one of them, leaving the other in place. + */ + func->pde(vmm, pgd, pdei); + } + + /* GPU may have cached the PTs, flush before freeing. */ + nvkm_vmm_flush_mark(it); + nvkm_vmm_flush(it); + } else { + /* PD has no valid PDEs left, so we can just destroy it. */ + nvkm_vmm_unref_pdes(it); + } + + /* Destroy PD/PT. */ + TRA(it, "PDE free %s", nvkm_vmm_desc_type(&desc[it->lvl - 1])); + nvkm_mmu_ptc_put(vmm->mmu, vmm->bootstrapped, &pt); + if (!pgt->refs[!type]) + nvkm_vmm_pt_del(&pgt); + it->lvl--; +} + +static void +nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt, + const struct nvkm_vmm_desc *desc, u32 ptei, u32 ptes) +{ + const struct nvkm_vmm_desc *pair = it->page[-1].desc; + const u32 sptb = desc->bits - pair->bits; + const u32 sptn = 1 << sptb; + struct nvkm_vmm *vmm = it->vmm; + u32 spti = ptei & (sptn - 1), lpti, pteb; + + /* Determine how many SPTEs are being touched under each LPTE, + * and drop reference counts. + */ + for (lpti = ptei >> sptb; ptes; spti = 0, lpti++) { + const u32 pten = min(sptn - spti, ptes); + pgt->pte[lpti] -= pten; + ptes -= pten; + } + + /* We're done here if there's no corresponding LPT. */ + if (!pgt->refs[0]) + return; + + for (ptei = pteb = ptei >> sptb; ptei < lpti; pteb = ptei) { + /* Skip over any LPTEs that still have valid SPTEs. */ + if (pgt->pte[pteb] & NVKM_VMM_PTE_SPTES) { + for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { + if (!(pgt->pte[ptei] & NVKM_VMM_PTE_SPTES)) + break; + } + continue; + } + + /* As there's no more non-UNMAPPED SPTEs left in the range + * covered by a number of LPTEs, the LPTEs once again take + * control over their address range. + * + * Determine how many LPTEs need to transition state. + */ + pgt->pte[ptei] &= ~NVKM_VMM_PTE_VALID; + for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { + if (pgt->pte[ptei] & NVKM_VMM_PTE_SPTES) + break; + pgt->pte[ptei] &= ~NVKM_VMM_PTE_VALID; + } + + if (pgt->pte[pteb] & NVKM_VMM_PTE_SPARSE) { + TRA(it, "LPTE %05x: U -> S %d PTEs", pteb, ptes); + pair->func->sparse(vmm, pgt->pt[0], pteb, ptes); + } else + if (pair->func->invalid) { + /* If the MMU supports it, restore the LPTE to the + * INVALID state to tell the MMU there is no point + * trying to fetch the corresponding SPTEs. + */ + TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes); + pair->func->invalid(vmm, pgt->pt[0], pteb, ptes); + } + } +} + +static bool +nvkm_vmm_unref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) +{ + const struct nvkm_vmm_desc *desc = it->desc; + const int type = desc->type == SPT; + struct nvkm_vmm_pt *pgt = it->pt[0]; + bool dma; + + if (pfn) { + /* Need to clear PTE valid bits before we dma_unmap_page(). */ + dma = desc->func->pfn_clear(it->vmm, pgt->pt[type], ptei, ptes); + if (dma) { + /* GPU may have cached the PT, flush before unmap. */ + nvkm_vmm_flush_mark(it); + nvkm_vmm_flush(it); + desc->func->pfn_unmap(it->vmm, pgt->pt[type], ptei, ptes); + } + } + + /* Drop PTE references. */ + pgt->refs[type] -= ptes; + + /* Dual-PTs need special handling, unless PDE becoming invalid. */ + if (desc->type == SPT && (pgt->refs[0] || pgt->refs[1])) + nvkm_vmm_unref_sptes(it, pgt, desc, ptei, ptes); + + /* PT no longer needed? Destroy it. */ + if (!pgt->refs[type]) { + it->lvl++; + TRA(it, "%s empty", nvkm_vmm_desc_type(desc)); + it->lvl--; + nvkm_vmm_unref_pdes(it); + return false; /* PTE writes for unmap() not necessary. */ + } + + return true; +} + +static void +nvkm_vmm_ref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt, + const struct nvkm_vmm_desc *desc, u32 ptei, u32 ptes) +{ + const struct nvkm_vmm_desc *pair = it->page[-1].desc; + const u32 sptb = desc->bits - pair->bits; + const u32 sptn = 1 << sptb; + struct nvkm_vmm *vmm = it->vmm; + u32 spti = ptei & (sptn - 1), lpti, pteb; + + /* Determine how many SPTEs are being touched under each LPTE, + * and increase reference counts. + */ + for (lpti = ptei >> sptb; ptes; spti = 0, lpti++) { + const u32 pten = min(sptn - spti, ptes); + pgt->pte[lpti] += pten; + ptes -= pten; + } + + /* We're done here if there's no corresponding LPT. */ + if (!pgt->refs[0]) + return; + + for (ptei = pteb = ptei >> sptb; ptei < lpti; pteb = ptei) { + /* Skip over any LPTEs that already have valid SPTEs. */ + if (pgt->pte[pteb] & NVKM_VMM_PTE_VALID) { + for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { + if (!(pgt->pte[ptei] & NVKM_VMM_PTE_VALID)) + break; + } + continue; + } + + /* As there are now non-UNMAPPED SPTEs in the range covered + * by a number of LPTEs, we need to transfer control of the + * address range to the SPTEs. + * + * Determine how many LPTEs need to transition state. + */ + pgt->pte[ptei] |= NVKM_VMM_PTE_VALID; + for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { + if (pgt->pte[ptei] & NVKM_VMM_PTE_VALID) + break; + pgt->pte[ptei] |= NVKM_VMM_PTE_VALID; + } + + if (pgt->pte[pteb] & NVKM_VMM_PTE_SPARSE) { + const u32 spti = pteb * sptn; + const u32 sptc = ptes * sptn; + /* The entire LPTE is marked as sparse, we need + * to make sure that the SPTEs are too. + */ + TRA(it, "SPTE %05x: U -> S %d PTEs", spti, sptc); + desc->func->sparse(vmm, pgt->pt[1], spti, sptc); + /* Sparse LPTEs prevent SPTEs from being accessed. */ + TRA(it, "LPTE %05x: S -> U %d PTEs", pteb, ptes); + pair->func->unmap(vmm, pgt->pt[0], pteb, ptes); + } else + if (pair->func->invalid) { + /* MMU supports blocking SPTEs by marking an LPTE + * as INVALID. We need to reverse that here. + */ + TRA(it, "LPTE %05x: I -> U %d PTEs", pteb, ptes); + pair->func->unmap(vmm, pgt->pt[0], pteb, ptes); + } + } +} + +static bool +nvkm_vmm_ref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) +{ + const struct nvkm_vmm_desc *desc = it->desc; + const int type = desc->type == SPT; + struct nvkm_vmm_pt *pgt = it->pt[0]; + + /* Take PTE references. */ + pgt->refs[type] += ptes; + + /* Dual-PTs need special handling. */ + if (desc->type == SPT) + nvkm_vmm_ref_sptes(it, pgt, desc, ptei, ptes); + + return true; +} + +static void +nvkm_vmm_sparse_ptes(const struct nvkm_vmm_desc *desc, + struct nvkm_vmm_pt *pgt, u32 ptei, u32 ptes) +{ + if (desc->type == PGD) { + while (ptes--) + pgt->pde[ptei++] = NVKM_VMM_PDE_SPARSE; + } else + if (desc->type == LPT) { + memset(&pgt->pte[ptei], NVKM_VMM_PTE_SPARSE, ptes); + } +} + +static bool +nvkm_vmm_sparse_unref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) +{ + struct nvkm_vmm_pt *pt = it->pt[0]; + if (it->desc->type == PGD) + memset(&pt->pde[ptei], 0x00, sizeof(pt->pde[0]) * ptes); + else + if (it->desc->type == LPT) + memset(&pt->pte[ptei], 0x00, sizeof(pt->pte[0]) * ptes); + return nvkm_vmm_unref_ptes(it, pfn, ptei, ptes); +} + +static bool +nvkm_vmm_sparse_ref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) +{ + nvkm_vmm_sparse_ptes(it->desc, it->pt[0], ptei, ptes); + return nvkm_vmm_ref_ptes(it, pfn, ptei, ptes); +} + +static bool +nvkm_vmm_ref_hwpt(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgd, u32 pdei) +{ + const struct nvkm_vmm_desc *desc = &it->desc[it->lvl - 1]; + const int type = desc->type == SPT; + struct nvkm_vmm_pt *pgt = pgd->pde[pdei]; + const bool zero = !pgt->sparse && !desc->func->invalid; + struct nvkm_vmm *vmm = it->vmm; + struct nvkm_mmu *mmu = vmm->mmu; + struct nvkm_mmu_pt *pt; + u32 pten = 1 << desc->bits; + u32 pteb, ptei, ptes; + u32 size = desc->size * pten; + + pgd->refs[0]++; + + pgt->pt[type] = nvkm_mmu_ptc_get(mmu, size, desc->align, zero); + if (!pgt->pt[type]) { + it->lvl--; + nvkm_vmm_unref_pdes(it); + return false; + } + + if (zero) + goto done; + + pt = pgt->pt[type]; + + if (desc->type == LPT && pgt->refs[1]) { + /* SPT already exists covering the same range as this LPT, + * which means we need to be careful that any LPTEs which + * overlap valid SPTEs are unmapped as opposed to invalid + * or sparse, which would prevent the MMU from looking at + * the SPTEs on some GPUs. + */ + for (ptei = pteb = 0; ptei < pten; pteb = ptei) { + bool spte = pgt->pte[ptei] & NVKM_VMM_PTE_SPTES; + for (ptes = 1, ptei++; ptei < pten; ptes++, ptei++) { + bool next = pgt->pte[ptei] & NVKM_VMM_PTE_SPTES; + if (spte != next) + break; + } + + if (!spte) { + if (pgt->sparse) + desc->func->sparse(vmm, pt, pteb, ptes); + else + desc->func->invalid(vmm, pt, pteb, ptes); + memset(&pgt->pte[pteb], 0x00, ptes); + } else { + desc->func->unmap(vmm, pt, pteb, ptes); + while (ptes--) + pgt->pte[pteb++] |= NVKM_VMM_PTE_VALID; + } + } + } else { + if (pgt->sparse) { + nvkm_vmm_sparse_ptes(desc, pgt, 0, pten); + desc->func->sparse(vmm, pt, 0, pten); + } else { + desc->func->invalid(vmm, pt, 0, pten); + } + } + +done: + TRA(it, "PDE write %s", nvkm_vmm_desc_type(desc)); + it->desc[it->lvl].func->pde(it->vmm, pgd, pdei); + nvkm_vmm_flush_mark(it); + return true; +} + +static bool +nvkm_vmm_ref_swpt(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgd, u32 pdei) +{ + const struct nvkm_vmm_desc *desc = &it->desc[it->lvl - 1]; + struct nvkm_vmm_pt *pgt = pgd->pde[pdei]; + + pgt = nvkm_vmm_pt_new(desc, NVKM_VMM_PDE_SPARSED(pgt), it->page); + if (!pgt) { + if (!pgd->refs[0]) + nvkm_vmm_unref_pdes(it); + return false; + } + + pgd->pde[pdei] = pgt; + return true; +} + +static inline u64 +nvkm_vmm_iter(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size, const char *name, bool ref, bool pfn, + bool (*REF_PTES)(struct nvkm_vmm_iter *, bool pfn, u32, u32), + nvkm_vmm_pte_func MAP_PTES, struct nvkm_vmm_map *map, + nvkm_vmm_pxe_func CLR_PTES) +{ + const struct nvkm_vmm_desc *desc = page->desc; + struct nvkm_vmm_iter it; + u64 bits = addr >> page->shift; + + it.page = page; + it.desc = desc; + it.vmm = vmm; + it.cnt = size >> page->shift; + it.flush = NVKM_VMM_LEVELS_MAX; + + /* Deconstruct address into PTE indices for each mapping level. */ + for (it.lvl = 0; desc[it.lvl].bits; it.lvl++) { + it.pte[it.lvl] = bits & ((1 << desc[it.lvl].bits) - 1); + bits >>= desc[it.lvl].bits; + } + it.max = --it.lvl; + it.pt[it.max] = vmm->pd; + + it.lvl = 0; + TRA(&it, "%s: %016llx %016llx %d %lld PTEs", name, + addr, size, page->shift, it.cnt); + it.lvl = it.max; + + /* Depth-first traversal of page tables. */ + while (it.cnt) { + struct nvkm_vmm_pt *pgt = it.pt[it.lvl]; + const int type = desc->type == SPT; + const u32 pten = 1 << desc->bits; + const u32 ptei = it.pte[0]; + const u32 ptes = min_t(u64, it.cnt, pten - ptei); + + /* Walk down the tree, finding page tables for each level. */ + for (; it.lvl; it.lvl--) { + const u32 pdei = it.pte[it.lvl]; + struct nvkm_vmm_pt *pgd = pgt; + + /* Software PT. */ + if (ref && NVKM_VMM_PDE_INVALID(pgd->pde[pdei])) { + if (!nvkm_vmm_ref_swpt(&it, pgd, pdei)) + goto fail; + } + it.pt[it.lvl - 1] = pgt = pgd->pde[pdei]; + + /* Hardware PT. + * + * This is a separate step from above due to GF100 and + * newer having dual page tables at some levels, which + * are refcounted independently. + */ + if (ref && !pgt->refs[desc[it.lvl - 1].type == SPT]) { + if (!nvkm_vmm_ref_hwpt(&it, pgd, pdei)) + goto fail; + } + } + + /* Handle PTE updates. */ + if (!REF_PTES || REF_PTES(&it, pfn, ptei, ptes)) { + struct nvkm_mmu_pt *pt = pgt->pt[type]; + if (MAP_PTES || CLR_PTES) { + if (MAP_PTES) + MAP_PTES(vmm, pt, ptei, ptes, map); + else + CLR_PTES(vmm, pt, ptei, ptes); + nvkm_vmm_flush_mark(&it); + } + } + + /* Walk back up the tree to the next position. */ + it.pte[it.lvl] += ptes; + it.cnt -= ptes; + if (it.cnt) { + while (it.pte[it.lvl] == (1 << desc[it.lvl].bits)) { + it.pte[it.lvl++] = 0; + it.pte[it.lvl]++; + } + } + } + + nvkm_vmm_flush(&it); + return ~0ULL; + +fail: + /* Reconstruct the failure address so the caller is able to + * reverse any partially completed operations. + */ + addr = it.pte[it.max--]; + do { + addr = addr << desc[it.max].bits; + addr |= it.pte[it.max]; + } while (it.max--); + + return addr << page->shift; +} + +static void +nvkm_vmm_ptes_sparse_put(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size) +{ + nvkm_vmm_iter(vmm, page, addr, size, "sparse unref", false, false, + nvkm_vmm_sparse_unref_ptes, NULL, NULL, + page->desc->func->invalid ? + page->desc->func->invalid : page->desc->func->unmap); +} + +static int +nvkm_vmm_ptes_sparse_get(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size) +{ + if ((page->type & NVKM_VMM_PAGE_SPARSE)) { + u64 fail = nvkm_vmm_iter(vmm, page, addr, size, "sparse ref", + true, false, nvkm_vmm_sparse_ref_ptes, + NULL, NULL, page->desc->func->sparse); + if (fail != ~0ULL) { + if ((size = fail - addr)) + nvkm_vmm_ptes_sparse_put(vmm, page, addr, size); + return -ENOMEM; + } + return 0; + } + return -EINVAL; +} + +static int +nvkm_vmm_ptes_sparse(struct nvkm_vmm *vmm, u64 addr, u64 size, bool ref) +{ + const struct nvkm_vmm_page *page = vmm->func->page; + int m = 0, i; + u64 start = addr; + u64 block; + + while (size) { + /* Limit maximum page size based on remaining size. */ + while (size < (1ULL << page[m].shift)) + m++; + i = m; + + /* Find largest page size suitable for alignment. */ + while (!IS_ALIGNED(addr, 1ULL << page[i].shift)) + i++; + + /* Determine number of PTEs at this page size. */ + if (i != m) { + /* Limited to alignment boundary of next page size. */ + u64 next = 1ULL << page[i - 1].shift; + u64 part = ALIGN(addr, next) - addr; + if (size - part >= next) + block = (part >> page[i].shift) << page[i].shift; + else + block = (size >> page[i].shift) << page[i].shift; + } else { + block = (size >> page[i].shift) << page[i].shift; + } + + /* Perform operation. */ + if (ref) { + int ret = nvkm_vmm_ptes_sparse_get(vmm, &page[i], addr, block); + if (ret) { + if ((size = addr - start)) + nvkm_vmm_ptes_sparse(vmm, start, size, false); + return ret; + } + } else { + nvkm_vmm_ptes_sparse_put(vmm, &page[i], addr, block); + } + + size -= block; + addr += block; + } + + return 0; +} + +static void +nvkm_vmm_ptes_unmap_put(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size, bool sparse, bool pfn) +{ + const struct nvkm_vmm_desc_func *func = page->desc->func; + nvkm_vmm_iter(vmm, page, addr, size, "unmap + unref", + false, pfn, nvkm_vmm_unref_ptes, NULL, NULL, + sparse ? func->sparse : func->invalid ? func->invalid : + func->unmap); +} + +static int +nvkm_vmm_ptes_get_map(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size, struct nvkm_vmm_map *map, + nvkm_vmm_pte_func func) +{ + u64 fail = nvkm_vmm_iter(vmm, page, addr, size, "ref + map", true, + false, nvkm_vmm_ref_ptes, func, map, NULL); + if (fail != ~0ULL) { + if ((size = fail - addr)) + nvkm_vmm_ptes_unmap_put(vmm, page, addr, size, false, false); + return -ENOMEM; + } + return 0; +} + +static void +nvkm_vmm_ptes_unmap(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size, bool sparse, bool pfn) +{ + const struct nvkm_vmm_desc_func *func = page->desc->func; + nvkm_vmm_iter(vmm, page, addr, size, "unmap", false, pfn, + NULL, NULL, NULL, + sparse ? func->sparse : func->invalid ? func->invalid : + func->unmap); +} + +static void +nvkm_vmm_ptes_map(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size, struct nvkm_vmm_map *map, + nvkm_vmm_pte_func func) +{ + nvkm_vmm_iter(vmm, page, addr, size, "map", false, false, + NULL, func, map, NULL); +} + +static void +nvkm_vmm_ptes_put(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size) +{ + nvkm_vmm_iter(vmm, page, addr, size, "unref", false, false, + nvkm_vmm_unref_ptes, NULL, NULL, NULL); +} + +static int +nvkm_vmm_ptes_get(struct nvkm_vmm *vmm, const struct nvkm_vmm_page *page, + u64 addr, u64 size) +{ + u64 fail = nvkm_vmm_iter(vmm, page, addr, size, "ref", true, false, + nvkm_vmm_ref_ptes, NULL, NULL, NULL); + if (fail != ~0ULL) { + if (fail != addr) + nvkm_vmm_ptes_put(vmm, page, addr, fail - addr); + return -ENOMEM; + } + return 0; +} + +static inline struct nvkm_vma * +nvkm_vma_new(u64 addr, u64 size) +{ + struct nvkm_vma *vma = kzalloc(sizeof(*vma), GFP_KERNEL); + if (vma) { + vma->addr = addr; + vma->size = size; + vma->page = NVKM_VMA_PAGE_NONE; + vma->refd = NVKM_VMA_PAGE_NONE; + } + return vma; +} + +struct nvkm_vma * +nvkm_vma_tail(struct nvkm_vma *vma, u64 tail) +{ + struct nvkm_vma *new; + + BUG_ON(vma->size == tail); + + if (!(new = nvkm_vma_new(vma->addr + (vma->size - tail), tail))) + return NULL; + vma->size -= tail; + + new->mapref = vma->mapref; + new->sparse = vma->sparse; + new->page = vma->page; + new->refd = vma->refd; + new->used = vma->used; + new->part = vma->part; + new->busy = vma->busy; + new->mapped = vma->mapped; + list_add(&new->head, &vma->head); + return new; +} + +static inline void +nvkm_vmm_free_remove(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + rb_erase(&vma->tree, &vmm->free); +} + +static inline void +nvkm_vmm_free_delete(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + nvkm_vmm_free_remove(vmm, vma); + list_del(&vma->head); + kfree(vma); +} + +static void +nvkm_vmm_free_insert(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + struct rb_node **ptr = &vmm->free.rb_node; + struct rb_node *parent = NULL; + + while (*ptr) { + struct nvkm_vma *this = rb_entry(*ptr, typeof(*this), tree); + parent = *ptr; + if (vma->size < this->size) + ptr = &parent->rb_left; + else + if (vma->size > this->size) + ptr = &parent->rb_right; + else + if (vma->addr < this->addr) + ptr = &parent->rb_left; + else + if (vma->addr > this->addr) + ptr = &parent->rb_right; + else + BUG(); + } + + rb_link_node(&vma->tree, parent, ptr); + rb_insert_color(&vma->tree, &vmm->free); +} + +static inline void +nvkm_vmm_node_remove(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + rb_erase(&vma->tree, &vmm->root); +} + +static inline void +nvkm_vmm_node_delete(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + nvkm_vmm_node_remove(vmm, vma); + list_del(&vma->head); + kfree(vma); +} + +static void +nvkm_vmm_node_insert(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + struct rb_node **ptr = &vmm->root.rb_node; + struct rb_node *parent = NULL; + + while (*ptr) { + struct nvkm_vma *this = rb_entry(*ptr, typeof(*this), tree); + parent = *ptr; + if (vma->addr < this->addr) + ptr = &parent->rb_left; + else + if (vma->addr > this->addr) + ptr = &parent->rb_right; + else + BUG(); + } + + rb_link_node(&vma->tree, parent, ptr); + rb_insert_color(&vma->tree, &vmm->root); +} + +struct nvkm_vma * +nvkm_vmm_node_search(struct nvkm_vmm *vmm, u64 addr) +{ + struct rb_node *node = vmm->root.rb_node; + while (node) { + struct nvkm_vma *vma = rb_entry(node, typeof(*vma), tree); + if (addr < vma->addr) + node = node->rb_left; + else + if (addr >= vma->addr + vma->size) + node = node->rb_right; + else + return vma; + } + return NULL; +} + +#define node(root, dir) (((root)->head.dir == &vmm->list) ? NULL : \ + list_entry((root)->head.dir, struct nvkm_vma, head)) + +static struct nvkm_vma * +nvkm_vmm_node_merge(struct nvkm_vmm *vmm, struct nvkm_vma *prev, + struct nvkm_vma *vma, struct nvkm_vma *next, u64 size) +{ + if (next) { + if (vma->size == size) { + vma->size += next->size; + nvkm_vmm_node_delete(vmm, next); + if (prev) { + prev->size += vma->size; + nvkm_vmm_node_delete(vmm, vma); + return prev; + } + return vma; + } + BUG_ON(prev); + + nvkm_vmm_node_remove(vmm, next); + vma->size -= size; + next->addr -= size; + next->size += size; + nvkm_vmm_node_insert(vmm, next); + return next; + } + + if (prev) { + if (vma->size != size) { + nvkm_vmm_node_remove(vmm, vma); + prev->size += size; + vma->addr += size; + vma->size -= size; + nvkm_vmm_node_insert(vmm, vma); + } else { + prev->size += vma->size; + nvkm_vmm_node_delete(vmm, vma); + } + return prev; + } + + return vma; +} + +struct nvkm_vma * +nvkm_vmm_node_split(struct nvkm_vmm *vmm, + struct nvkm_vma *vma, u64 addr, u64 size) +{ + struct nvkm_vma *prev = NULL; + + if (vma->addr != addr) { + prev = vma; + if (!(vma = nvkm_vma_tail(vma, vma->size + vma->addr - addr))) + return NULL; + vma->part = true; + nvkm_vmm_node_insert(vmm, vma); + } + + if (vma->size != size) { + struct nvkm_vma *tmp; + if (!(tmp = nvkm_vma_tail(vma, vma->size - size))) { + nvkm_vmm_node_merge(vmm, prev, vma, NULL, vma->size); + return NULL; + } + tmp->part = true; + nvkm_vmm_node_insert(vmm, tmp); + } + + return vma; +} + +static void +nvkm_vma_dump(struct nvkm_vma *vma) +{ + printk(KERN_ERR "%016llx %016llx %c%c%c%c%c%c%c%c %p\n", + vma->addr, (u64)vma->size, + vma->used ? '-' : 'F', + vma->mapref ? 'R' : '-', + vma->sparse ? 'S' : '-', + vma->page != NVKM_VMA_PAGE_NONE ? '0' + vma->page : '-', + vma->refd != NVKM_VMA_PAGE_NONE ? '0' + vma->refd : '-', + vma->part ? 'P' : '-', + vma->busy ? 'B' : '-', + vma->mapped ? 'M' : '-', + vma->memory); +} + +static void +nvkm_vmm_dump(struct nvkm_vmm *vmm) +{ + struct nvkm_vma *vma; + list_for_each_entry(vma, &vmm->list, head) { + nvkm_vma_dump(vma); + } +} + +static void +nvkm_vmm_dtor(struct nvkm_vmm *vmm) +{ + struct nvkm_vma *vma; + struct rb_node *node; + + if (0) + nvkm_vmm_dump(vmm); + + while ((node = rb_first(&vmm->root))) { + struct nvkm_vma *vma = rb_entry(node, typeof(*vma), tree); + nvkm_vmm_put(vmm, &vma); + } + + if (vmm->bootstrapped) { + const struct nvkm_vmm_page *page = vmm->func->page; + const u64 limit = vmm->limit - vmm->start; + + while (page[1].shift) + page++; + + nvkm_mmu_ptc_dump(vmm->mmu); + nvkm_vmm_ptes_put(vmm, page, vmm->start, limit); + } + + vma = list_first_entry(&vmm->list, typeof(*vma), head); + list_del(&vma->head); + kfree(vma); + WARN_ON(!list_empty(&vmm->list)); + + if (vmm->nullp) { + dma_free_coherent(vmm->mmu->subdev.device->dev, 16 * 1024, + vmm->nullp, vmm->null); + } + + if (vmm->pd) { + nvkm_mmu_ptc_put(vmm->mmu, true, &vmm->pd->pt[0]); + nvkm_vmm_pt_del(&vmm->pd); + } +} + +static int +nvkm_vmm_ctor_managed(struct nvkm_vmm *vmm, u64 addr, u64 size) +{ + struct nvkm_vma *vma; + if (!(vma = nvkm_vma_new(addr, size))) + return -ENOMEM; + vma->mapref = true; + vma->sparse = false; + vma->used = true; + nvkm_vmm_node_insert(vmm, vma); + list_add_tail(&vma->head, &vmm->list); + return 0; +} + +static int +nvkm_vmm_ctor(const struct nvkm_vmm_func *func, struct nvkm_mmu *mmu, + u32 pd_header, bool managed, u64 addr, u64 size, + struct lock_class_key *key, const char *name, + struct nvkm_vmm *vmm) +{ + static struct lock_class_key _key; + const struct nvkm_vmm_page *page = func->page; + const struct nvkm_vmm_desc *desc; + struct nvkm_vma *vma; + int levels, bits = 0, ret; + + vmm->func = func; + vmm->mmu = mmu; + vmm->name = name; + vmm->debug = mmu->subdev.debug; + kref_init(&vmm->kref); + + __mutex_init(&vmm->mutex, "&vmm->mutex", key ? key : &_key); + + /* Locate the smallest page size supported by the backend, it will + * have the deepest nesting of page tables. + */ + while (page[1].shift) + page++; + + /* Locate the structure that describes the layout of the top-level + * page table, and determine the number of valid bits in a virtual + * address. + */ + for (levels = 0, desc = page->desc; desc->bits; desc++, levels++) + bits += desc->bits; + bits += page->shift; + desc--; + + if (WARN_ON(levels > NVKM_VMM_LEVELS_MAX)) + return -EINVAL; + + /* Allocate top-level page table. */ + vmm->pd = nvkm_vmm_pt_new(desc, false, NULL); + if (!vmm->pd) + return -ENOMEM; + vmm->pd->refs[0] = 1; + INIT_LIST_HEAD(&vmm->join); + + /* ... and the GPU storage for it, except on Tesla-class GPUs that + * have the PD embedded in the instance structure. + */ + if (desc->size) { + const u32 size = pd_header + desc->size * (1 << desc->bits); + vmm->pd->pt[0] = nvkm_mmu_ptc_get(mmu, size, desc->align, true); + if (!vmm->pd->pt[0]) + return -ENOMEM; + } + + /* Initialise address-space MM. */ + INIT_LIST_HEAD(&vmm->list); + vmm->free = RB_ROOT; + vmm->root = RB_ROOT; + + if (managed) { + /* Address-space will be managed by the client for the most + * part, except for a specified area where NVKM allocations + * are allowed to be placed. + */ + vmm->start = 0; + vmm->limit = 1ULL << bits; + if (addr + size < addr || addr + size > vmm->limit) + return -EINVAL; + + /* Client-managed area before the NVKM-managed area. */ + if (addr && (ret = nvkm_vmm_ctor_managed(vmm, 0, addr))) + return ret; + + /* NVKM-managed area. */ + if (size) { + if (!(vma = nvkm_vma_new(addr, size))) + return -ENOMEM; + nvkm_vmm_free_insert(vmm, vma); + list_add_tail(&vma->head, &vmm->list); + } + + /* Client-managed area after the NVKM-managed area. */ + addr = addr + size; + size = vmm->limit - addr; + if (size && (ret = nvkm_vmm_ctor_managed(vmm, addr, size))) + return ret; + } else { + /* Address-space fully managed by NVKM, requiring calls to + * nvkm_vmm_get()/nvkm_vmm_put() to allocate address-space. + */ + vmm->start = addr; + vmm->limit = size ? (addr + size) : (1ULL << bits); + if (vmm->start > vmm->limit || vmm->limit > (1ULL << bits)) + return -EINVAL; + + if (!(vma = nvkm_vma_new(vmm->start, vmm->limit - vmm->start))) + return -ENOMEM; + + nvkm_vmm_free_insert(vmm, vma); + list_add(&vma->head, &vmm->list); + } + + return 0; +} + +int +nvkm_vmm_new_(const struct nvkm_vmm_func *func, struct nvkm_mmu *mmu, + u32 hdr, bool managed, u64 addr, u64 size, + struct lock_class_key *key, const char *name, + struct nvkm_vmm **pvmm) +{ + if (!(*pvmm = kzalloc(sizeof(**pvmm), GFP_KERNEL))) + return -ENOMEM; + return nvkm_vmm_ctor(func, mmu, hdr, managed, addr, size, key, name, *pvmm); +} + +static struct nvkm_vma * +nvkm_vmm_pfn_split_merge(struct nvkm_vmm *vmm, struct nvkm_vma *vma, + u64 addr, u64 size, u8 page, bool map) +{ + struct nvkm_vma *prev = NULL; + struct nvkm_vma *next = NULL; + + if (vma->addr == addr && vma->part && (prev = node(vma, prev))) { + if (prev->memory || prev->mapped != map) + prev = NULL; + } + + if (vma->addr + vma->size == addr + size && (next = node(vma, next))) { + if (!next->part || + next->memory || next->mapped != map) + next = NULL; + } + + if (prev || next) + return nvkm_vmm_node_merge(vmm, prev, vma, next, size); + return nvkm_vmm_node_split(vmm, vma, addr, size); +} + +int +nvkm_vmm_pfn_unmap(struct nvkm_vmm *vmm, u64 addr, u64 size) +{ + struct nvkm_vma *vma = nvkm_vmm_node_search(vmm, addr); + struct nvkm_vma *next; + u64 limit = addr + size; + u64 start = addr; + + if (!vma) + return -EINVAL; + + do { + if (!vma->mapped || vma->memory) + continue; + + size = min(limit - start, vma->size - (start - vma->addr)); + + nvkm_vmm_ptes_unmap_put(vmm, &vmm->func->page[vma->refd], + start, size, false, true); + + next = nvkm_vmm_pfn_split_merge(vmm, vma, start, size, 0, false); + if (!WARN_ON(!next)) { + vma = next; + vma->refd = NVKM_VMA_PAGE_NONE; + vma->mapped = false; + } + } while ((vma = node(vma, next)) && (start = vma->addr) < limit); + + return 0; +} + +/*TODO: + * - Avoid PT readback (for dma_unmap etc), this might end up being dealt + * with inside HMM, which would be a lot nicer for us to deal with. + * - Support for systems without a 4KiB page size. + */ +int +nvkm_vmm_pfn_map(struct nvkm_vmm *vmm, u8 shift, u64 addr, u64 size, u64 *pfn) +{ + const struct nvkm_vmm_page *page = vmm->func->page; + struct nvkm_vma *vma, *tmp; + u64 limit = addr + size; + u64 start = addr; + int pm = size >> shift; + int pi = 0; + + /* Only support mapping where the page size of the incoming page + * array matches a page size available for direct mapping. + */ + while (page->shift && (page->shift != shift || + page->desc->func->pfn == NULL)) + page++; + + if (!page->shift || !IS_ALIGNED(addr, 1ULL << shift) || + !IS_ALIGNED(size, 1ULL << shift) || + addr + size < addr || addr + size > vmm->limit) { + VMM_DEBUG(vmm, "paged map %d %d %016llx %016llx\n", + shift, page->shift, addr, size); + return -EINVAL; + } + + if (!(vma = nvkm_vmm_node_search(vmm, addr))) + return -ENOENT; + + do { + bool map = !!(pfn[pi] & NVKM_VMM_PFN_V); + bool mapped = vma->mapped; + u64 size = limit - start; + u64 addr = start; + int pn, ret = 0; + + /* Narrow the operation window to cover a single action (page + * should be mapped or not) within a single VMA. + */ + for (pn = 0; pi + pn < pm; pn++) { + if (map != !!(pfn[pi + pn] & NVKM_VMM_PFN_V)) + break; + } + size = min_t(u64, size, pn << page->shift); + size = min_t(u64, size, vma->size + vma->addr - addr); + + /* Reject any operation to unmanaged regions, and areas that + * have nvkm_memory objects mapped in them already. + */ + if (!vma->mapref || vma->memory) { + ret = -EINVAL; + goto next; + } + + /* In order to both properly refcount GPU page tables, and + * prevent "normal" mappings and these direct mappings from + * interfering with each other, we need to track contiguous + * ranges that have been mapped with this interface. + * + * Here we attempt to either split an existing VMA so we're + * able to flag the region as either unmapped/mapped, or to + * merge with adjacent VMAs that are already compatible. + * + * If the region is already compatible, nothing is required. + */ + if (map != mapped) { + tmp = nvkm_vmm_pfn_split_merge(vmm, vma, addr, size, + page - + vmm->func->page, map); + if (WARN_ON(!tmp)) { + ret = -ENOMEM; + goto next; + } + + if ((tmp->mapped = map)) + tmp->refd = page - vmm->func->page; + else + tmp->refd = NVKM_VMA_PAGE_NONE; + vma = tmp; + } + + /* Update HW page tables. */ + if (map) { + struct nvkm_vmm_map args; + args.page = page; + args.pfn = &pfn[pi]; + + if (!mapped) { + ret = nvkm_vmm_ptes_get_map(vmm, page, addr, + size, &args, page-> + desc->func->pfn); + } else { + nvkm_vmm_ptes_map(vmm, page, addr, size, &args, + page->desc->func->pfn); + } + } else { + if (mapped) { + nvkm_vmm_ptes_unmap_put(vmm, page, addr, size, + false, true); + } + } + +next: + /* Iterate to next operation. */ + if (vma->addr + vma->size == addr + size) + vma = node(vma, next); + start += size; + + if (ret) { + /* Failure is signalled by clearing the valid bit on + * any PFN that couldn't be modified as requested. + */ + while (size) { + pfn[pi++] = NVKM_VMM_PFN_NONE; + size -= 1 << page->shift; + } + } else { + pi += size >> page->shift; + } + } while (vma && start < limit); + + return 0; +} + +void +nvkm_vmm_unmap_region(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + struct nvkm_vma *prev = NULL; + struct nvkm_vma *next; + + nvkm_memory_tags_put(vma->memory, vmm->mmu->subdev.device, &vma->tags); + nvkm_memory_unref(&vma->memory); + vma->mapped = false; + + if (vma->part && (prev = node(vma, prev)) && prev->mapped) + prev = NULL; + if ((next = node(vma, next)) && (!next->part || next->mapped)) + next = NULL; + nvkm_vmm_node_merge(vmm, prev, vma, next, vma->size); +} + +void +nvkm_vmm_unmap_locked(struct nvkm_vmm *vmm, struct nvkm_vma *vma, bool pfn) +{ + const struct nvkm_vmm_page *page = &vmm->func->page[vma->refd]; + + if (vma->mapref) { + nvkm_vmm_ptes_unmap_put(vmm, page, vma->addr, vma->size, vma->sparse, pfn); + vma->refd = NVKM_VMA_PAGE_NONE; + } else { + nvkm_vmm_ptes_unmap(vmm, page, vma->addr, vma->size, vma->sparse, pfn); + } + + nvkm_vmm_unmap_region(vmm, vma); +} + +void +nvkm_vmm_unmap(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + if (vma->memory) { + mutex_lock(&vmm->mutex); + nvkm_vmm_unmap_locked(vmm, vma, false); + mutex_unlock(&vmm->mutex); + } +} + +static int +nvkm_vmm_map_valid(struct nvkm_vmm *vmm, struct nvkm_vma *vma, + void *argv, u32 argc, struct nvkm_vmm_map *map) +{ + switch (nvkm_memory_target(map->memory)) { + case NVKM_MEM_TARGET_VRAM: + if (!(map->page->type & NVKM_VMM_PAGE_VRAM)) { + VMM_DEBUG(vmm, "%d !VRAM", map->page->shift); + return -EINVAL; + } + break; + case NVKM_MEM_TARGET_HOST: + case NVKM_MEM_TARGET_NCOH: + if (!(map->page->type & NVKM_VMM_PAGE_HOST)) { + VMM_DEBUG(vmm, "%d !HOST", map->page->shift); + return -EINVAL; + } + break; + default: + WARN_ON(1); + return -ENOSYS; + } + + if (!IS_ALIGNED( vma->addr, 1ULL << map->page->shift) || + !IS_ALIGNED((u64)vma->size, 1ULL << map->page->shift) || + !IS_ALIGNED( map->offset, 1ULL << map->page->shift) || + nvkm_memory_page(map->memory) < map->page->shift) { + VMM_DEBUG(vmm, "alignment %016llx %016llx %016llx %d %d", + vma->addr, (u64)vma->size, map->offset, map->page->shift, + nvkm_memory_page(map->memory)); + return -EINVAL; + } + + return vmm->func->valid(vmm, argv, argc, map); +} + +static int +nvkm_vmm_map_choose(struct nvkm_vmm *vmm, struct nvkm_vma *vma, + void *argv, u32 argc, struct nvkm_vmm_map *map) +{ + for (map->page = vmm->func->page; map->page->shift; map->page++) { + VMM_DEBUG(vmm, "trying %d", map->page->shift); + if (!nvkm_vmm_map_valid(vmm, vma, argv, argc, map)) + return 0; + } + return -EINVAL; +} + +static int +nvkm_vmm_map_locked(struct nvkm_vmm *vmm, struct nvkm_vma *vma, + void *argv, u32 argc, struct nvkm_vmm_map *map) +{ + nvkm_vmm_pte_func func; + int ret; + + /* Make sure we won't overrun the end of the memory object. */ + if (unlikely(nvkm_memory_size(map->memory) < map->offset + vma->size)) { + VMM_DEBUG(vmm, "overrun %016llx %016llx %016llx", + nvkm_memory_size(map->memory), + map->offset, (u64)vma->size); + return -EINVAL; + } + + /* Check remaining arguments for validity. */ + if (vma->page == NVKM_VMA_PAGE_NONE && + vma->refd == NVKM_VMA_PAGE_NONE) { + /* Find the largest page size we can perform the mapping at. */ + const u32 debug = vmm->debug; + vmm->debug = 0; + ret = nvkm_vmm_map_choose(vmm, vma, argv, argc, map); + vmm->debug = debug; + if (ret) { + VMM_DEBUG(vmm, "invalid at any page size"); + nvkm_vmm_map_choose(vmm, vma, argv, argc, map); + return -EINVAL; + } + } else { + /* Page size of the VMA is already pre-determined. */ + if (vma->refd != NVKM_VMA_PAGE_NONE) + map->page = &vmm->func->page[vma->refd]; + else + map->page = &vmm->func->page[vma->page]; + + ret = nvkm_vmm_map_valid(vmm, vma, argv, argc, map); + if (ret) { + VMM_DEBUG(vmm, "invalid %d\n", ret); + return ret; + } + } + + /* Deal with the 'offset' argument, and fetch the backend function. */ + map->off = map->offset; + if (map->mem) { + for (; map->off; map->mem = map->mem->next) { + u64 size = (u64)map->mem->length << NVKM_RAM_MM_SHIFT; + if (size > map->off) + break; + map->off -= size; + } + func = map->page->desc->func->mem; + } else + if (map->sgl) { + for (; map->off; map->sgl = sg_next(map->sgl)) { + u64 size = sg_dma_len(map->sgl); + if (size > map->off) + break; + map->off -= size; + } + func = map->page->desc->func->sgl; + } else { + map->dma += map->offset >> PAGE_SHIFT; + map->off = map->offset & PAGE_MASK; + func = map->page->desc->func->dma; + } + + /* Perform the map. */ + if (vma->refd == NVKM_VMA_PAGE_NONE) { + ret = nvkm_vmm_ptes_get_map(vmm, map->page, vma->addr, vma->size, map, func); + if (ret) + return ret; + + vma->refd = map->page - vmm->func->page; + } else { + nvkm_vmm_ptes_map(vmm, map->page, vma->addr, vma->size, map, func); + } + + nvkm_memory_tags_put(vma->memory, vmm->mmu->subdev.device, &vma->tags); + nvkm_memory_unref(&vma->memory); + vma->memory = nvkm_memory_ref(map->memory); + vma->mapped = true; + vma->tags = map->tags; + return 0; +} + +int +nvkm_vmm_map(struct nvkm_vmm *vmm, struct nvkm_vma *vma, void *argv, u32 argc, + struct nvkm_vmm_map *map) +{ + int ret; + mutex_lock(&vmm->mutex); + ret = nvkm_vmm_map_locked(vmm, vma, argv, argc, map); + vma->busy = false; + mutex_unlock(&vmm->mutex); + return ret; +} + +static void +nvkm_vmm_put_region(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + struct nvkm_vma *prev, *next; + + if ((prev = node(vma, prev)) && !prev->used) { + vma->addr = prev->addr; + vma->size += prev->size; + nvkm_vmm_free_delete(vmm, prev); + } + + if ((next = node(vma, next)) && !next->used) { + vma->size += next->size; + nvkm_vmm_free_delete(vmm, next); + } + + nvkm_vmm_free_insert(vmm, vma); +} + +void +nvkm_vmm_put_locked(struct nvkm_vmm *vmm, struct nvkm_vma *vma) +{ + const struct nvkm_vmm_page *page = vmm->func->page; + struct nvkm_vma *next = vma; + + BUG_ON(vma->part); + + if (vma->mapref || !vma->sparse) { + do { + const bool mem = next->memory != NULL; + const bool map = next->mapped; + const u8 refd = next->refd; + const u64 addr = next->addr; + u64 size = next->size; + + /* Merge regions that are in the same state. */ + while ((next = node(next, next)) && next->part && + (next->mapped == map) && + (next->memory != NULL) == mem && + (next->refd == refd)) + size += next->size; + + if (map) { + /* Region(s) are mapped, merge the unmap + * and dereference into a single walk of + * the page tree. + */ + nvkm_vmm_ptes_unmap_put(vmm, &page[refd], addr, + size, vma->sparse, + !mem); + } else + if (refd != NVKM_VMA_PAGE_NONE) { + /* Drop allocation-time PTE references. */ + nvkm_vmm_ptes_put(vmm, &page[refd], addr, size); + } + } while (next && next->part); + } + + /* Merge any mapped regions that were split from the initial + * address-space allocation back into the allocated VMA, and + * release memory/compression resources. + */ + next = vma; + do { + if (next->mapped) + nvkm_vmm_unmap_region(vmm, next); + } while ((next = node(vma, next)) && next->part); + + if (vma->sparse && !vma->mapref) { + /* Sparse region that was allocated with a fixed page size, + * meaning all relevant PTEs were referenced once when the + * region was allocated, and remained that way, regardless + * of whether memory was mapped into it afterwards. + * + * The process of unmapping, unsparsing, and dereferencing + * PTEs can be done in a single page tree walk. + */ + nvkm_vmm_ptes_sparse_put(vmm, &page[vma->refd], vma->addr, vma->size); + } else + if (vma->sparse) { + /* Sparse region that wasn't allocated with a fixed page size, + * PTE references were taken both at allocation time (to make + * the GPU see the region as sparse), and when mapping memory + * into the region. + * + * The latter was handled above, and the remaining references + * are dealt with here. + */ + nvkm_vmm_ptes_sparse(vmm, vma->addr, vma->size, false); + } + + /* Remove VMA from the list of allocated nodes. */ + nvkm_vmm_node_remove(vmm, vma); + + /* Merge VMA back into the free list. */ + vma->page = NVKM_VMA_PAGE_NONE; + vma->refd = NVKM_VMA_PAGE_NONE; + vma->used = false; + nvkm_vmm_put_region(vmm, vma); +} + +void +nvkm_vmm_put(struct nvkm_vmm *vmm, struct nvkm_vma **pvma) +{ + struct nvkm_vma *vma = *pvma; + if (vma) { + mutex_lock(&vmm->mutex); + nvkm_vmm_put_locked(vmm, vma); + mutex_unlock(&vmm->mutex); + *pvma = NULL; + } +} + +int +nvkm_vmm_get_locked(struct nvkm_vmm *vmm, bool getref, bool mapref, bool sparse, + u8 shift, u8 align, u64 size, struct nvkm_vma **pvma) +{ + const struct nvkm_vmm_page *page = &vmm->func->page[NVKM_VMA_PAGE_NONE]; + struct rb_node *node = NULL, *temp; + struct nvkm_vma *vma = NULL, *tmp; + u64 addr, tail; + int ret; + + VMM_TRACE(vmm, "getref %d mapref %d sparse %d " + "shift: %d align: %d size: %016llx", + getref, mapref, sparse, shift, align, size); + + /* Zero-sized, or lazily-allocated sparse VMAs, make no sense. */ + if (unlikely(!size || (!getref && !mapref && sparse))) { + VMM_DEBUG(vmm, "args %016llx %d %d %d", + size, getref, mapref, sparse); + return -EINVAL; + } + + /* Tesla-class GPUs can only select page size per-PDE, which means + * we're required to know the mapping granularity up-front to find + * a suitable region of address-space. + * + * The same goes if we're requesting up-front allocation of PTES. + */ + if (unlikely((getref || vmm->func->page_block) && !shift)) { + VMM_DEBUG(vmm, "page size required: %d %016llx", + getref, vmm->func->page_block); + return -EINVAL; + } + + /* If a specific page size was requested, determine its index and + * make sure the requested size is a multiple of the page size. + */ + if (shift) { + for (page = vmm->func->page; page->shift; page++) { + if (shift == page->shift) + break; + } + + if (!page->shift || !IS_ALIGNED(size, 1ULL << page->shift)) { + VMM_DEBUG(vmm, "page %d %016llx", shift, size); + return -EINVAL; + } + align = max_t(u8, align, shift); + } else { + align = max_t(u8, align, 12); + } + + /* Locate smallest block that can possibly satisfy the allocation. */ + temp = vmm->free.rb_node; + while (temp) { + struct nvkm_vma *this = rb_entry(temp, typeof(*this), tree); + if (this->size < size) { + temp = temp->rb_right; + } else { + node = temp; + temp = temp->rb_left; + } + } + + if (unlikely(!node)) + return -ENOSPC; + + /* Take into account alignment restrictions, trying larger blocks + * in turn until we find a suitable free block. + */ + do { + struct nvkm_vma *this = rb_entry(node, typeof(*this), tree); + struct nvkm_vma *prev = node(this, prev); + struct nvkm_vma *next = node(this, next); + const int p = page - vmm->func->page; + + addr = this->addr; + if (vmm->func->page_block && prev && prev->page != p) + addr = ALIGN(addr, vmm->func->page_block); + addr = ALIGN(addr, 1ULL << align); + + tail = this->addr + this->size; + if (vmm->func->page_block && next && next->page != p) + tail = ALIGN_DOWN(tail, vmm->func->page_block); + + if (addr <= tail && tail - addr >= size) { + nvkm_vmm_free_remove(vmm, this); + vma = this; + break; + } + } while ((node = rb_next(node))); + + if (unlikely(!vma)) + return -ENOSPC; + + /* If the VMA we found isn't already exactly the requested size, + * it needs to be split, and the remaining free blocks returned. + */ + if (addr != vma->addr) { + if (!(tmp = nvkm_vma_tail(vma, vma->size + vma->addr - addr))) { + nvkm_vmm_put_region(vmm, vma); + return -ENOMEM; + } + nvkm_vmm_free_insert(vmm, vma); + vma = tmp; + } + + if (size != vma->size) { + if (!(tmp = nvkm_vma_tail(vma, vma->size - size))) { + nvkm_vmm_put_region(vmm, vma); + return -ENOMEM; + } + nvkm_vmm_free_insert(vmm, tmp); + } + + /* Pre-allocate page tables and/or setup sparse mappings. */ + if (sparse && getref) + ret = nvkm_vmm_ptes_sparse_get(vmm, page, vma->addr, vma->size); + else if (sparse) + ret = nvkm_vmm_ptes_sparse(vmm, vma->addr, vma->size, true); + else if (getref) + ret = nvkm_vmm_ptes_get(vmm, page, vma->addr, vma->size); + else + ret = 0; + if (ret) { + nvkm_vmm_put_region(vmm, vma); + return ret; + } + + vma->mapref = mapref && !getref; + vma->sparse = sparse; + vma->page = page - vmm->func->page; + vma->refd = getref ? vma->page : NVKM_VMA_PAGE_NONE; + vma->used = true; + nvkm_vmm_node_insert(vmm, vma); + *pvma = vma; + return 0; +} + +int +nvkm_vmm_get(struct nvkm_vmm *vmm, u8 page, u64 size, struct nvkm_vma **pvma) +{ + int ret; + mutex_lock(&vmm->mutex); + ret = nvkm_vmm_get_locked(vmm, false, true, false, page, 0, size, pvma); + mutex_unlock(&vmm->mutex); + return ret; +} + +void +nvkm_vmm_part(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + if (inst && vmm && vmm->func->part) { + mutex_lock(&vmm->mutex); + vmm->func->part(vmm, inst); + mutex_unlock(&vmm->mutex); + } +} + +int +nvkm_vmm_join(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + int ret = 0; + if (vmm->func->join) { + mutex_lock(&vmm->mutex); + ret = vmm->func->join(vmm, inst); + mutex_unlock(&vmm->mutex); + } + return ret; +} + +static bool +nvkm_vmm_boot_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) +{ + const struct nvkm_vmm_desc *desc = it->desc; + const int type = desc->type == SPT; + nvkm_memory_boot(it->pt[0]->pt[type]->memory, it->vmm); + return false; +} + +int +nvkm_vmm_boot(struct nvkm_vmm *vmm) +{ + const struct nvkm_vmm_page *page = vmm->func->page; + const u64 limit = vmm->limit - vmm->start; + int ret; + + while (page[1].shift) + page++; + + ret = nvkm_vmm_ptes_get(vmm, page, vmm->start, limit); + if (ret) + return ret; + + nvkm_vmm_iter(vmm, page, vmm->start, limit, "bootstrap", false, false, + nvkm_vmm_boot_ptes, NULL, NULL, NULL); + vmm->bootstrapped = true; + return 0; +} + +static void +nvkm_vmm_del(struct kref *kref) +{ + struct nvkm_vmm *vmm = container_of(kref, typeof(*vmm), kref); + nvkm_vmm_dtor(vmm); + kfree(vmm); +} + +void +nvkm_vmm_unref(struct nvkm_vmm **pvmm) +{ + struct nvkm_vmm *vmm = *pvmm; + if (vmm) { + kref_put(&vmm->kref, nvkm_vmm_del); + *pvmm = NULL; + } +} + +struct nvkm_vmm * +nvkm_vmm_ref(struct nvkm_vmm *vmm) +{ + if (vmm) + kref_get(&vmm->kref); + return vmm; +} + +int +nvkm_vmm_new(struct nvkm_device *device, u64 addr, u64 size, void *argv, + u32 argc, struct lock_class_key *key, const char *name, + struct nvkm_vmm **pvmm) +{ + struct nvkm_mmu *mmu = device->mmu; + struct nvkm_vmm *vmm = NULL; + int ret; + ret = mmu->func->vmm.ctor(mmu, false, addr, size, argv, argc, + key, name, &vmm); + if (ret) + nvkm_vmm_unref(&vmm); + *pvmm = vmm; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h new file mode 100644 index 000000000..f6188aa91 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h @@ -0,0 +1,355 @@ +#ifndef __NVKM_VMM_H__ +#define __NVKM_VMM_H__ +#include "priv.h" +#include +enum nvkm_memory_target; + +struct nvkm_vmm_pt { + /* Some GPUs have a mapping level with a dual page tables to + * support large and small pages in the same address-range. + * + * We track the state of both page tables in one place, which + * is why there's multiple PT pointers/refcounts here. + */ + struct nvkm_mmu_pt *pt[2]; + u32 refs[2]; + + /* Page size handled by this PT. + * + * Tesla backend needs to know this when writinge PDEs, + * otherwise unnecessary. + */ + u8 page; + + /* Entire page table sparse. + * + * Used to propagate sparseness to child page tables. + */ + bool sparse:1; + + /* Tracking for page directories. + * + * The array is indexed by PDE, and will either point to the + * child page table, or indicate the PDE is marked as sparse. + **/ +#define NVKM_VMM_PDE_INVALID(pde) IS_ERR_OR_NULL(pde) +#define NVKM_VMM_PDE_SPARSED(pde) IS_ERR(pde) +#define NVKM_VMM_PDE_SPARSE ERR_PTR(-EBUSY) + struct nvkm_vmm_pt **pde; + + /* Tracking for dual page tables. + * + * There's one entry for each LPTE, keeping track of whether + * there are valid SPTEs in the same address-range. + * + * This information is used to manage LPTE state transitions. + */ +#define NVKM_VMM_PTE_SPARSE 0x80 +#define NVKM_VMM_PTE_VALID 0x40 +#define NVKM_VMM_PTE_SPTES 0x3f + u8 pte[]; +}; + +typedef void (*nvkm_vmm_pxe_func)(struct nvkm_vmm *, + struct nvkm_mmu_pt *, u32 ptei, u32 ptes); +typedef void (*nvkm_vmm_pde_func)(struct nvkm_vmm *, + struct nvkm_vmm_pt *, u32 pdei); +typedef void (*nvkm_vmm_pte_func)(struct nvkm_vmm *, struct nvkm_mmu_pt *, + u32 ptei, u32 ptes, struct nvkm_vmm_map *); + +struct nvkm_vmm_desc_func { + nvkm_vmm_pxe_func invalid; + nvkm_vmm_pxe_func unmap; + nvkm_vmm_pxe_func sparse; + + nvkm_vmm_pde_func pde; + + nvkm_vmm_pte_func mem; + nvkm_vmm_pte_func dma; + nvkm_vmm_pte_func sgl; + + nvkm_vmm_pte_func pfn; + bool (*pfn_clear)(struct nvkm_vmm *, struct nvkm_mmu_pt *, u32 ptei, u32 ptes); + nvkm_vmm_pxe_func pfn_unmap; +}; + +extern const struct nvkm_vmm_desc_func gf100_vmm_pgd; +void gf100_vmm_pgd_pde(struct nvkm_vmm *, struct nvkm_vmm_pt *, u32); +extern const struct nvkm_vmm_desc_func gf100_vmm_pgt; +void gf100_vmm_pgt_unmap(struct nvkm_vmm *, struct nvkm_mmu_pt *, u32, u32); +void gf100_vmm_pgt_mem(struct nvkm_vmm *, struct nvkm_mmu_pt *, u32, u32, + struct nvkm_vmm_map *); +void gf100_vmm_pgt_dma(struct nvkm_vmm *, struct nvkm_mmu_pt *, u32, u32, + struct nvkm_vmm_map *); +void gf100_vmm_pgt_sgl(struct nvkm_vmm *, struct nvkm_mmu_pt *, u32, u32, + struct nvkm_vmm_map *); + +void gk104_vmm_lpt_invalid(struct nvkm_vmm *, struct nvkm_mmu_pt *, u32, u32); + +struct nvkm_vmm_desc { + enum { + PGD, + PGT, + SPT, + LPT, + } type; + u8 bits; /* VMA bits covered by PT. */ + u8 size; /* Bytes-per-PTE. */ + u32 align; /* PT address alignment. */ + const struct nvkm_vmm_desc_func *func; +}; + +extern const struct nvkm_vmm_desc nv50_vmm_desc_12[]; +extern const struct nvkm_vmm_desc nv50_vmm_desc_16[]; + +extern const struct nvkm_vmm_desc gk104_vmm_desc_16_12[]; +extern const struct nvkm_vmm_desc gk104_vmm_desc_16_16[]; +extern const struct nvkm_vmm_desc gk104_vmm_desc_17_12[]; +extern const struct nvkm_vmm_desc gk104_vmm_desc_17_17[]; + +extern const struct nvkm_vmm_desc gm200_vmm_desc_16_12[]; +extern const struct nvkm_vmm_desc gm200_vmm_desc_16_16[]; +extern const struct nvkm_vmm_desc gm200_vmm_desc_17_12[]; +extern const struct nvkm_vmm_desc gm200_vmm_desc_17_17[]; + +extern const struct nvkm_vmm_desc gp100_vmm_desc_12[]; +extern const struct nvkm_vmm_desc gp100_vmm_desc_16[]; + +struct nvkm_vmm_page { + u8 shift; + const struct nvkm_vmm_desc *desc; +#define NVKM_VMM_PAGE_SPARSE 0x01 +#define NVKM_VMM_PAGE_VRAM 0x02 +#define NVKM_VMM_PAGE_HOST 0x04 +#define NVKM_VMM_PAGE_COMP 0x08 +#define NVKM_VMM_PAGE_Sxxx (NVKM_VMM_PAGE_SPARSE) +#define NVKM_VMM_PAGE_xVxx (NVKM_VMM_PAGE_VRAM) +#define NVKM_VMM_PAGE_SVxx (NVKM_VMM_PAGE_Sxxx | NVKM_VMM_PAGE_VRAM) +#define NVKM_VMM_PAGE_xxHx (NVKM_VMM_PAGE_HOST) +#define NVKM_VMM_PAGE_SxHx (NVKM_VMM_PAGE_Sxxx | NVKM_VMM_PAGE_HOST) +#define NVKM_VMM_PAGE_xVHx (NVKM_VMM_PAGE_xVxx | NVKM_VMM_PAGE_HOST) +#define NVKM_VMM_PAGE_SVHx (NVKM_VMM_PAGE_SVxx | NVKM_VMM_PAGE_HOST) +#define NVKM_VMM_PAGE_xVxC (NVKM_VMM_PAGE_xVxx | NVKM_VMM_PAGE_COMP) +#define NVKM_VMM_PAGE_SVxC (NVKM_VMM_PAGE_SVxx | NVKM_VMM_PAGE_COMP) +#define NVKM_VMM_PAGE_xxHC (NVKM_VMM_PAGE_xxHx | NVKM_VMM_PAGE_COMP) +#define NVKM_VMM_PAGE_SxHC (NVKM_VMM_PAGE_SxHx | NVKM_VMM_PAGE_COMP) + u8 type; +}; + +struct nvkm_vmm_func { + int (*join)(struct nvkm_vmm *, struct nvkm_memory *inst); + void (*part)(struct nvkm_vmm *, struct nvkm_memory *inst); + + int (*aper)(enum nvkm_memory_target); + int (*valid)(struct nvkm_vmm *, void *argv, u32 argc, + struct nvkm_vmm_map *); + void (*flush)(struct nvkm_vmm *, int depth); + + int (*mthd)(struct nvkm_vmm *, struct nvkm_client *, + u32 mthd, void *argv, u32 argc); + + void (*invalidate_pdb)(struct nvkm_vmm *, u64 addr); + + u64 page_block; + const struct nvkm_vmm_page page[]; +}; + +struct nvkm_vmm_join { + struct nvkm_memory *inst; + struct list_head head; +}; + +int nvkm_vmm_new_(const struct nvkm_vmm_func *, struct nvkm_mmu *, + u32 pd_header, bool managed, u64 addr, u64 size, + struct lock_class_key *, const char *name, + struct nvkm_vmm **); +struct nvkm_vma *nvkm_vmm_node_search(struct nvkm_vmm *, u64 addr); +struct nvkm_vma *nvkm_vmm_node_split(struct nvkm_vmm *, struct nvkm_vma *, + u64 addr, u64 size); +int nvkm_vmm_get_locked(struct nvkm_vmm *, bool getref, bool mapref, + bool sparse, u8 page, u8 align, u64 size, + struct nvkm_vma **pvma); +void nvkm_vmm_put_locked(struct nvkm_vmm *, struct nvkm_vma *); +void nvkm_vmm_unmap_locked(struct nvkm_vmm *, struct nvkm_vma *, bool pfn); +void nvkm_vmm_unmap_region(struct nvkm_vmm *, struct nvkm_vma *); + +#define NVKM_VMM_PFN_ADDR 0xfffffffffffff000ULL +#define NVKM_VMM_PFN_ADDR_SHIFT 12 +#define NVKM_VMM_PFN_APER 0x00000000000000f0ULL +#define NVKM_VMM_PFN_HOST 0x0000000000000000ULL +#define NVKM_VMM_PFN_VRAM 0x0000000000000010ULL +#define NVKM_VMM_PFN_A 0x0000000000000004ULL +#define NVKM_VMM_PFN_W 0x0000000000000002ULL +#define NVKM_VMM_PFN_V 0x0000000000000001ULL +#define NVKM_VMM_PFN_NONE 0x0000000000000000ULL + +int nvkm_vmm_pfn_map(struct nvkm_vmm *, u8 page, u64 addr, u64 size, u64 *pfn); +int nvkm_vmm_pfn_unmap(struct nvkm_vmm *, u64 addr, u64 size); + +struct nvkm_vma *nvkm_vma_tail(struct nvkm_vma *, u64 tail); + +int nv04_vmm_new_(const struct nvkm_vmm_func *, struct nvkm_mmu *, u32, + bool, u64, u64, void *, u32, struct lock_class_key *, + const char *, struct nvkm_vmm **); +int nv04_vmm_valid(struct nvkm_vmm *, void *, u32, struct nvkm_vmm_map *); + +int nv50_vmm_join(struct nvkm_vmm *, struct nvkm_memory *); +void nv50_vmm_part(struct nvkm_vmm *, struct nvkm_memory *); +int nv50_vmm_valid(struct nvkm_vmm *, void *, u32, struct nvkm_vmm_map *); +void nv50_vmm_flush(struct nvkm_vmm *, int); + +int gf100_vmm_new_(const struct nvkm_vmm_func *, const struct nvkm_vmm_func *, + struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int gf100_vmm_join_(struct nvkm_vmm *, struct nvkm_memory *, u64 base); +int gf100_vmm_join(struct nvkm_vmm *, struct nvkm_memory *); +void gf100_vmm_part(struct nvkm_vmm *, struct nvkm_memory *); +int gf100_vmm_aper(enum nvkm_memory_target); +int gf100_vmm_valid(struct nvkm_vmm *, void *, u32, struct nvkm_vmm_map *); +void gf100_vmm_flush(struct nvkm_vmm *, int); +void gf100_vmm_invalidate(struct nvkm_vmm *, u32 type); +void gf100_vmm_invalidate_pdb(struct nvkm_vmm *, u64 addr); + +int gk20a_vmm_aper(enum nvkm_memory_target); + +int gm200_vmm_new_(const struct nvkm_vmm_func *, const struct nvkm_vmm_func *, + struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int gm200_vmm_join_(struct nvkm_vmm *, struct nvkm_memory *, u64 base); +int gm200_vmm_join(struct nvkm_vmm *, struct nvkm_memory *); + +int gp100_vmm_new_(const struct nvkm_vmm_func *, + struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int gp100_vmm_join(struct nvkm_vmm *, struct nvkm_memory *); +int gp100_vmm_valid(struct nvkm_vmm *, void *, u32, struct nvkm_vmm_map *); +void gp100_vmm_flush(struct nvkm_vmm *, int); +int gp100_vmm_mthd(struct nvkm_vmm *, struct nvkm_client *, u32, void *, u32); +void gp100_vmm_invalidate_pdb(struct nvkm_vmm *, u64 addr); + +int gv100_vmm_join(struct nvkm_vmm *, struct nvkm_memory *); + +int nv04_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int nv41_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int nv44_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int nv50_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int mcp77_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int g84_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int gf100_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int gk104_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int gk20a_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, struct nvkm_vmm **); +int gm200_vmm_new_fixed(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); +int gm200_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); +int gm20b_vmm_new_fixed(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); +int gm20b_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); +int gp100_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); +int gp10b_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); +int gv100_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); +int tu102_vmm_new(struct nvkm_mmu *, bool, u64, u64, void *, u32, + struct lock_class_key *, const char *, + struct nvkm_vmm **); + +#define VMM_PRINT(l,v,p,f,a...) do { \ + struct nvkm_vmm *_vmm = (v); \ + if (CONFIG_NOUVEAU_DEBUG >= (l) && _vmm->debug >= (l)) { \ + nvkm_printk_(&_vmm->mmu->subdev, 0, p, "%s: "f"\n", \ + _vmm->name, ##a); \ + } \ +} while(0) +#define VMM_DEBUG(v,f,a...) VMM_PRINT(NV_DBG_DEBUG, (v), info, f, ##a) +#define VMM_TRACE(v,f,a...) VMM_PRINT(NV_DBG_TRACE, (v), info, f, ##a) +#define VMM_SPAM(v,f,a...) VMM_PRINT(NV_DBG_SPAM , (v), dbg, f, ##a) + +#define VMM_MAP_ITER(VMM,PT,PTEI,PTEN,MAP,FILL,BASE,SIZE,NEXT) do { \ + nvkm_kmap((PT)->memory); \ + while (PTEN) { \ + u64 _ptes = ((SIZE) - MAP->off) >> MAP->page->shift; \ + u64 _addr = ((BASE) + MAP->off); \ + \ + if (_ptes > PTEN) { \ + MAP->off += PTEN << MAP->page->shift; \ + _ptes = PTEN; \ + } else { \ + MAP->off = 0; \ + NEXT; \ + } \ + \ + VMM_SPAM(VMM, "ITER %08x %08x PTE(s)", PTEI, (u32)_ptes); \ + \ + FILL(VMM, PT, PTEI, _ptes, MAP, _addr); \ + PTEI += _ptes; \ + PTEN -= _ptes; \ + } \ + nvkm_done((PT)->memory); \ +} while(0) + +#define VMM_MAP_ITER_MEM(VMM,PT,PTEI,PTEN,MAP,FILL) \ + VMM_MAP_ITER(VMM,PT,PTEI,PTEN,MAP,FILL, \ + ((u64)MAP->mem->offset << NVKM_RAM_MM_SHIFT), \ + ((u64)MAP->mem->length << NVKM_RAM_MM_SHIFT), \ + (MAP->mem = MAP->mem->next)) +#define VMM_MAP_ITER_DMA(VMM,PT,PTEI,PTEN,MAP,FILL) \ + VMM_MAP_ITER(VMM,PT,PTEI,PTEN,MAP,FILL, \ + *MAP->dma, PAGE_SIZE, MAP->dma++) +#define VMM_MAP_ITER_SGL(VMM,PT,PTEI,PTEN,MAP,FILL) \ + VMM_MAP_ITER(VMM,PT,PTEI,PTEN,MAP,FILL, \ + sg_dma_address(MAP->sgl), sg_dma_len(MAP->sgl), \ + (MAP->sgl = sg_next(MAP->sgl))) + +#define VMM_FO(m,o,d,c,b) nvkm_fo##b((m)->memory, (o), (d), (c)) +#define VMM_WO(m,o,d,c,b) nvkm_wo##b((m)->memory, (o), (d)) +#define VMM_XO(m,v,o,d,c,b,fn,f,a...) do { \ + const u32 _pteo = (o); u##b _data = (d); \ + VMM_SPAM((v), " %010llx "f, (m)->addr + _pteo, _data, ##a); \ + VMM_##fn((m), (m)->base + _pteo, _data, (c), b); \ +} while(0) + +#define VMM_WO032(m,v,o,d) VMM_XO((m),(v),(o),(d), 1, 32, WO, "%08x") +#define VMM_FO032(m,v,o,d,c) \ + VMM_XO((m),(v),(o),(d),(c), 32, FO, "%08x %08x", (c)) + +#define VMM_WO064(m,v,o,d) VMM_XO((m),(v),(o),(d), 1, 64, WO, "%016llx") +#define VMM_FO064(m,v,o,d,c) \ + VMM_XO((m),(v),(o),(d),(c), 64, FO, "%016llx %08x", (c)) + +#define VMM_XO128(m,v,o,lo,hi,c,f,a...) do { \ + u32 _pteo = (o), _ptes = (c); \ + const u64 _addr = (m)->addr + _pteo; \ + VMM_SPAM((v), " %010llx %016llx%016llx"f, _addr, (hi), (lo), ##a); \ + while (_ptes--) { \ + nvkm_wo64((m)->memory, (m)->base + _pteo + 0, (lo)); \ + nvkm_wo64((m)->memory, (m)->base + _pteo + 8, (hi)); \ + _pteo += 0x10; \ + } \ +} while(0) + +#define VMM_WO128(m,v,o,lo,hi) VMM_XO128((m),(v),(o),(lo),(hi), 1, "") +#define VMM_FO128(m,v,o,lo,hi,c) do { \ + nvkm_kmap((m)->memory); \ + VMM_XO128((m),(v),(o),(lo),(hi),(c), " %08x", (c)); \ + nvkm_done((m)->memory); \ +} while(0) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgf100.c new file mode 100644 index 000000000..5438384d9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgf100.c @@ -0,0 +1,424 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include +#include +#include + +#include +#include + +static inline void +gf100_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr) +{ + u64 base = (addr >> 8) | map->type; + u64 data = base; + + if (map->ctag && !(map->next & (1ULL << 44))) { + while (ptes--) { + data = base | ((map->ctag >> 1) << 44); + if (!(map->ctag++ & 1)) + data |= BIT_ULL(60); + + VMM_WO064(pt, vmm, ptei++ * 8, data); + base += map->next; + } + } else { + map->type += ptes * map->ctag; + + while (ptes--) { + VMM_WO064(pt, vmm, ptei++ * 8, data); + data += map->next; + } + } +} + +void +gf100_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, gf100_vmm_pgt_pte); +} + +void +gf100_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + if (map->page->shift == PAGE_SHIFT) { + VMM_SPAM(vmm, "DMAA %08x %08x PTE(s)", ptei, ptes); + nvkm_kmap(pt->memory); + while (ptes--) { + const u64 data = (*map->dma++ >> 8) | map->type; + VMM_WO064(pt, vmm, ptei++ * 8, data); + map->type += map->ctag; + } + nvkm_done(pt->memory); + return; + } + + VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, gf100_vmm_pgt_pte); +} + +void +gf100_vmm_pgt_mem(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_MEM(vmm, pt, ptei, ptes, map, gf100_vmm_pgt_pte); +} + +void +gf100_vmm_pgt_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + VMM_FO064(pt, vmm, ptei * 8, 0ULL, ptes); +} + +const struct nvkm_vmm_desc_func +gf100_vmm_pgt = { + .unmap = gf100_vmm_pgt_unmap, + .mem = gf100_vmm_pgt_mem, + .dma = gf100_vmm_pgt_dma, + .sgl = gf100_vmm_pgt_sgl, +}; + +void +gf100_vmm_pgd_pde(struct nvkm_vmm *vmm, struct nvkm_vmm_pt *pgd, u32 pdei) +{ + struct nvkm_vmm_pt *pgt = pgd->pde[pdei]; + struct nvkm_mmu_pt *pd = pgd->pt[0]; + struct nvkm_mmu_pt *pt; + u64 data = 0; + + if ((pt = pgt->pt[0])) { + switch (nvkm_memory_target(pt->memory)) { + case NVKM_MEM_TARGET_VRAM: data |= 1ULL << 0; break; + case NVKM_MEM_TARGET_HOST: data |= 2ULL << 0; + data |= BIT_ULL(35); /* VOL */ + break; + case NVKM_MEM_TARGET_NCOH: data |= 3ULL << 0; break; + default: + WARN_ON(1); + return; + } + data |= pt->addr >> 8; + } + + if ((pt = pgt->pt[1])) { + switch (nvkm_memory_target(pt->memory)) { + case NVKM_MEM_TARGET_VRAM: data |= 1ULL << 32; break; + case NVKM_MEM_TARGET_HOST: data |= 2ULL << 32; + data |= BIT_ULL(34); /* VOL */ + break; + case NVKM_MEM_TARGET_NCOH: data |= 3ULL << 32; break; + default: + WARN_ON(1); + return; + } + data |= pt->addr << 24; + } + + nvkm_kmap(pd->memory); + VMM_WO064(pd, vmm, pdei * 8, data); + nvkm_done(pd->memory); +} + +const struct nvkm_vmm_desc_func +gf100_vmm_pgd = { + .unmap = gf100_vmm_pgt_unmap, + .pde = gf100_vmm_pgd_pde, +}; + +static const struct nvkm_vmm_desc +gf100_vmm_desc_17_12[] = { + { SPT, 15, 8, 0x1000, &gf100_vmm_pgt }, + { PGD, 13, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +static const struct nvkm_vmm_desc +gf100_vmm_desc_17_17[] = { + { LPT, 10, 8, 0x1000, &gf100_vmm_pgt }, + { PGD, 13, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +static const struct nvkm_vmm_desc +gf100_vmm_desc_16_12[] = { + { SPT, 14, 8, 0x1000, &gf100_vmm_pgt }, + { PGD, 14, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +static const struct nvkm_vmm_desc +gf100_vmm_desc_16_16[] = { + { LPT, 10, 8, 0x1000, &gf100_vmm_pgt }, + { PGD, 14, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +void +gf100_vmm_invalidate_pdb(struct nvkm_vmm *vmm, u64 addr) +{ + struct nvkm_device *device = vmm->mmu->subdev.device; + nvkm_wr32(device, 0x100cb8, addr); +} + +void +gf100_vmm_invalidate(struct nvkm_vmm *vmm, u32 type) +{ + struct nvkm_device *device = vmm->mmu->subdev.device; + struct nvkm_mmu_pt *pd = vmm->pd->pt[0]; + u64 addr = 0; + + mutex_lock(&vmm->mmu->mutex); + /* Looks like maybe a "free flush slots" counter, the + * faster you write to 0x100cbc to more it decreases. + */ + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x100c80) & 0x00ff0000) + break; + ); + + if (!(type & 0x00000002) /* ALL_PDB. */) { + switch (nvkm_memory_target(pd->memory)) { + case NVKM_MEM_TARGET_VRAM: addr |= 0x00000000; break; + case NVKM_MEM_TARGET_HOST: addr |= 0x00000002; break; + case NVKM_MEM_TARGET_NCOH: addr |= 0x00000003; break; + default: + WARN_ON(1); + break; + } + addr |= (vmm->pd->pt[0]->addr >> 12) << 4; + + vmm->func->invalidate_pdb(vmm, addr); + } + + nvkm_wr32(device, 0x100cbc, 0x80000000 | type); + + /* Wait for flush to be queued? */ + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x100c80) & 0x00008000) + break; + ); + mutex_unlock(&vmm->mmu->mutex); +} + +void +gf100_vmm_flush(struct nvkm_vmm *vmm, int depth) +{ + u32 type = 0x00000001; /* PAGE_ALL */ + if (atomic_read(&vmm->engref[NVKM_SUBDEV_BAR])) + type |= 0x00000004; /* HUB_ONLY */ + gf100_vmm_invalidate(vmm, type); +} + +int +gf100_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc, + struct nvkm_vmm_map *map) +{ + const enum nvkm_memory_target target = nvkm_memory_target(map->memory); + const struct nvkm_vmm_page *page = map->page; + const bool gm20x = page->desc->func->sparse != NULL; + union { + struct gf100_vmm_map_vn vn; + struct gf100_vmm_map_v0 v0; + } *args = argv; + struct nvkm_device *device = vmm->mmu->subdev.device; + struct nvkm_memory *memory = map->memory; + u8 kind, kind_inv, priv, ro, vol; + int kindn, aper, ret = -ENOSYS; + const u8 *kindm; + + map->next = (1 << page->shift) >> 8; + map->type = map->ctag = 0; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + vol = !!args->v0.vol; + ro = !!args->v0.ro; + priv = !!args->v0.priv; + kind = args->v0.kind; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + vol = target == NVKM_MEM_TARGET_HOST; + ro = 0; + priv = 0; + kind = 0x00; + } else { + VMM_DEBUG(vmm, "args"); + return ret; + } + + aper = vmm->func->aper(target); + if (WARN_ON(aper < 0)) + return aper; + + kindm = vmm->mmu->func->kind(vmm->mmu, &kindn, &kind_inv); + if (kind >= kindn || kindm[kind] == kind_inv) { + VMM_DEBUG(vmm, "kind %02x", kind); + return -EINVAL; + } + + if (kindm[kind] != kind) { + u32 comp = (page->shift == 16 && !gm20x) ? 16 : 17; + u32 tags = ALIGN(nvkm_memory_size(memory), 1 << 17) >> comp; + if (aper != 0 || !(page->type & NVKM_VMM_PAGE_COMP)) { + VMM_DEBUG(vmm, "comp %d %02x", aper, page->type); + return -EINVAL; + } + + ret = nvkm_memory_tags_get(memory, device, tags, + nvkm_ltc_tags_clear, + &map->tags); + if (ret) { + VMM_DEBUG(vmm, "comp %d", ret); + return ret; + } + + if (map->tags->mn) { + u64 tags = map->tags->mn->offset + (map->offset >> 17); + if (page->shift == 17 || !gm20x) { + map->type |= tags << 44; + map->ctag |= 1ULL << 44; + map->next |= 1ULL << 44; + } else { + map->ctag |= tags << 1 | 1; + } + } else { + kind = kindm[kind]; + } + } + + map->type |= BIT(0); + map->type |= (u64)priv << 1; + map->type |= (u64) ro << 2; + map->type |= (u64) vol << 32; + map->type |= (u64)aper << 33; + map->type |= (u64)kind << 36; + return 0; +} + +int +gf100_vmm_aper(enum nvkm_memory_target target) +{ + switch (target) { + case NVKM_MEM_TARGET_VRAM: return 0; + case NVKM_MEM_TARGET_HOST: return 2; + case NVKM_MEM_TARGET_NCOH: return 3; + default: + return -EINVAL; + } +} + +void +gf100_vmm_part(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + nvkm_fo64(inst, 0x0200, 0x00000000, 2); +} + +int +gf100_vmm_join_(struct nvkm_vmm *vmm, struct nvkm_memory *inst, u64 base) +{ + struct nvkm_mmu_pt *pd = vmm->pd->pt[0]; + + switch (nvkm_memory_target(pd->memory)) { + case NVKM_MEM_TARGET_VRAM: base |= 0ULL << 0; break; + case NVKM_MEM_TARGET_HOST: base |= 2ULL << 0; + base |= BIT_ULL(2) /* VOL. */; + break; + case NVKM_MEM_TARGET_NCOH: base |= 3ULL << 0; break; + default: + WARN_ON(1); + return -EINVAL; + } + base |= pd->addr; + + nvkm_kmap(inst); + nvkm_wo64(inst, 0x0200, base); + nvkm_wo64(inst, 0x0208, vmm->limit - 1); + nvkm_done(inst); + return 0; +} + +int +gf100_vmm_join(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + return gf100_vmm_join_(vmm, inst, 0); +} + +static const struct nvkm_vmm_func +gf100_vmm_17 = { + .join = gf100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 17, &gf100_vmm_desc_17_17[0], NVKM_VMM_PAGE_xVxC }, + { 12, &gf100_vmm_desc_17_12[0], NVKM_VMM_PAGE_xVHx }, + {} + } +}; + +static const struct nvkm_vmm_func +gf100_vmm_16 = { + .join = gf100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 16, &gf100_vmm_desc_16_16[0], NVKM_VMM_PAGE_xVxC }, + { 12, &gf100_vmm_desc_16_12[0], NVKM_VMM_PAGE_xVHx }, + {} + } +}; + +int +gf100_vmm_new_(const struct nvkm_vmm_func *func_16, + const struct nvkm_vmm_func *func_17, + struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + switch (mmu->subdev.device->fb->page) { + case 16: return nv04_vmm_new_(func_16, mmu, 0, managed, addr, size, + argv, argc, key, name, pvmm); + case 17: return nv04_vmm_new_(func_17, mmu, 0, managed, addr, size, + argv, argc, key, name, pvmm); + default: + WARN_ON(1); + return -EINVAL; + } +} + +int +gf100_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gf100_vmm_new_(&gf100_vmm_16, &gf100_vmm_17, mmu, managed, addr, + size, argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk104.c new file mode 100644 index 000000000..0b59c01fd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk104.c @@ -0,0 +1,104 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +void +gk104_vmm_lpt_invalid(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + /* VALID_FALSE + PRIV tells the MMU to ignore corresponding SPTEs. */ + VMM_FO064(pt, vmm, ptei * 8, BIT_ULL(1) /* PRIV. */, ptes); +} + +static const struct nvkm_vmm_desc_func +gk104_vmm_lpt = { + .invalid = gk104_vmm_lpt_invalid, + .unmap = gf100_vmm_pgt_unmap, + .mem = gf100_vmm_pgt_mem, +}; + +const struct nvkm_vmm_desc +gk104_vmm_desc_17_12[] = { + { SPT, 15, 8, 0x1000, &gf100_vmm_pgt }, + { PGD, 13, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +const struct nvkm_vmm_desc +gk104_vmm_desc_17_17[] = { + { LPT, 10, 8, 0x1000, &gk104_vmm_lpt }, + { PGD, 13, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +const struct nvkm_vmm_desc +gk104_vmm_desc_16_12[] = { + { SPT, 14, 8, 0x1000, &gf100_vmm_pgt }, + { PGD, 14, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +const struct nvkm_vmm_desc +gk104_vmm_desc_16_16[] = { + { LPT, 10, 8, 0x1000, &gk104_vmm_lpt }, + { PGD, 14, 8, 0x1000, &gf100_vmm_pgd }, + {} +}; + +static const struct nvkm_vmm_func +gk104_vmm_17 = { + .join = gf100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 17, &gk104_vmm_desc_17_17[0], NVKM_VMM_PAGE_xVxC }, + { 12, &gk104_vmm_desc_17_12[0], NVKM_VMM_PAGE_xVHx }, + {} + } +}; + +static const struct nvkm_vmm_func +gk104_vmm_16 = { + .join = gf100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 16, &gk104_vmm_desc_16_16[0], NVKM_VMM_PAGE_xVxC }, + { 12, &gk104_vmm_desc_16_12[0], NVKM_VMM_PAGE_xVHx }, + {} + } +}; + +int +gk104_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gf100_vmm_new_(&gk104_vmm_16, &gk104_vmm_17, mmu, managed, addr, + size, argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk20a.c new file mode 100644 index 000000000..5a9582dce --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgk20a.c @@ -0,0 +1,73 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include + +int +gk20a_vmm_aper(enum nvkm_memory_target target) +{ + switch (target) { + case NVKM_MEM_TARGET_NCOH: return 0; + default: + return -EINVAL; + } +} + +static const struct nvkm_vmm_func +gk20a_vmm_17 = { + .join = gf100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 17, &gk104_vmm_desc_17_17[0], NVKM_VMM_PAGE_xxHC }, + { 12, &gk104_vmm_desc_17_12[0], NVKM_VMM_PAGE_xxHx }, + {} + } +}; + +static const struct nvkm_vmm_func +gk20a_vmm_16 = { + .join = gf100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 16, &gk104_vmm_desc_16_16[0], NVKM_VMM_PAGE_xxHC }, + { 12, &gk104_vmm_desc_16_12[0], NVKM_VMM_PAGE_xxHx }, + {} + } +}; + +int +gk20a_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gf100_vmm_new_(&gk20a_vmm_16, &gk20a_vmm_17, mmu, managed, addr, + size, argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm200.c new file mode 100644 index 000000000..2e61af02d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm200.c @@ -0,0 +1,187 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include +#include + +static void +gm200_vmm_pgt_sparse(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + /* VALID_FALSE + VOL tells the MMU to treat the PTE as sparse. */ + VMM_FO064(pt, vmm, ptei * 8, BIT_ULL(32) /* VOL. */, ptes); +} + +static const struct nvkm_vmm_desc_func +gm200_vmm_spt = { + .unmap = gf100_vmm_pgt_unmap, + .sparse = gm200_vmm_pgt_sparse, + .mem = gf100_vmm_pgt_mem, + .dma = gf100_vmm_pgt_dma, + .sgl = gf100_vmm_pgt_sgl, +}; + +static const struct nvkm_vmm_desc_func +gm200_vmm_lpt = { + .invalid = gk104_vmm_lpt_invalid, + .unmap = gf100_vmm_pgt_unmap, + .sparse = gm200_vmm_pgt_sparse, + .mem = gf100_vmm_pgt_mem, +}; + +static void +gm200_vmm_pgd_sparse(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 pdei, u32 pdes) +{ + /* VALID_FALSE + VOL_BIG tells the MMU to treat the PDE as sparse. */ + VMM_FO064(pt, vmm, pdei * 8, BIT_ULL(35) /* VOL_BIG. */, pdes); +} + +static const struct nvkm_vmm_desc_func +gm200_vmm_pgd = { + .unmap = gf100_vmm_pgt_unmap, + .sparse = gm200_vmm_pgd_sparse, + .pde = gf100_vmm_pgd_pde, +}; + +const struct nvkm_vmm_desc +gm200_vmm_desc_17_12[] = { + { SPT, 15, 8, 0x1000, &gm200_vmm_spt }, + { PGD, 13, 8, 0x1000, &gm200_vmm_pgd }, + {} +}; + +const struct nvkm_vmm_desc +gm200_vmm_desc_17_17[] = { + { LPT, 10, 8, 0x1000, &gm200_vmm_lpt }, + { PGD, 13, 8, 0x1000, &gm200_vmm_pgd }, + {} +}; + +const struct nvkm_vmm_desc +gm200_vmm_desc_16_12[] = { + { SPT, 14, 8, 0x1000, &gm200_vmm_spt }, + { PGD, 14, 8, 0x1000, &gm200_vmm_pgd }, + {} +}; + +const struct nvkm_vmm_desc +gm200_vmm_desc_16_16[] = { + { LPT, 10, 8, 0x1000, &gm200_vmm_lpt }, + { PGD, 14, 8, 0x1000, &gm200_vmm_pgd }, + {} +}; + +int +gm200_vmm_join_(struct nvkm_vmm *vmm, struct nvkm_memory *inst, u64 base) +{ + if (vmm->func->page[1].shift == 16) + base |= BIT_ULL(11); + return gf100_vmm_join_(vmm, inst, base); +} + +int +gm200_vmm_join(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + return gm200_vmm_join_(vmm, inst, 0); +} + +static const struct nvkm_vmm_func +gm200_vmm_17 = { + .join = gm200_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 27, &gm200_vmm_desc_17_17[1], NVKM_VMM_PAGE_Sxxx }, + { 17, &gm200_vmm_desc_17_17[0], NVKM_VMM_PAGE_SVxC }, + { 12, &gm200_vmm_desc_17_12[0], NVKM_VMM_PAGE_SVHx }, + {} + } +}; + +static const struct nvkm_vmm_func +gm200_vmm_16 = { + .join = gm200_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 27, &gm200_vmm_desc_16_16[1], NVKM_VMM_PAGE_Sxxx }, + { 16, &gm200_vmm_desc_16_16[0], NVKM_VMM_PAGE_SVxC }, + { 12, &gm200_vmm_desc_16_12[0], NVKM_VMM_PAGE_SVHx }, + {} + } +}; + +int +gm200_vmm_new_(const struct nvkm_vmm_func *func_16, + const struct nvkm_vmm_func *func_17, + struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + const struct nvkm_vmm_func *func; + union { + struct gm200_vmm_vn vn; + struct gm200_vmm_v0 v0; + } *args = argv; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + switch (args->v0.bigpage) { + case 16: func = func_16; break; + case 17: func = func_17; break; + default: + return -EINVAL; + } + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + func = func_17; + } else + return ret; + + return nvkm_vmm_new_(func, mmu, 0, managed, addr, size, key, name, pvmm); +} + +int +gm200_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gm200_vmm_new_(&gm200_vmm_16, &gm200_vmm_17, mmu, managed, addr, + size, argv, argc, key, name, pvmm); +} + +int +gm200_vmm_new_fixed(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gf100_vmm_new_(&gm200_vmm_16, &gm200_vmm_17, mmu, managed, addr, + size, argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm20b.c new file mode 100644 index 000000000..96b759695 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgm20b.c @@ -0,0 +1,72 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +static const struct nvkm_vmm_func +gm20b_vmm_17 = { + .join = gm200_vmm_join, + .part = gf100_vmm_part, + .aper = gk20a_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 27, &gm200_vmm_desc_17_17[1], NVKM_VMM_PAGE_Sxxx }, + { 17, &gm200_vmm_desc_17_17[0], NVKM_VMM_PAGE_SxHC }, + { 12, &gm200_vmm_desc_17_12[0], NVKM_VMM_PAGE_SxHx }, + {} + } +}; + +static const struct nvkm_vmm_func +gm20b_vmm_16 = { + .join = gm200_vmm_join, + .part = gf100_vmm_part, + .aper = gk20a_vmm_aper, + .valid = gf100_vmm_valid, + .flush = gf100_vmm_flush, + .invalidate_pdb = gf100_vmm_invalidate_pdb, + .page = { + { 27, &gm200_vmm_desc_16_16[1], NVKM_VMM_PAGE_Sxxx }, + { 16, &gm200_vmm_desc_16_16[0], NVKM_VMM_PAGE_SxHC }, + { 12, &gm200_vmm_desc_16_12[0], NVKM_VMM_PAGE_SxHx }, + {} + } +}; + +int +gm20b_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gm200_vmm_new_(&gm20b_vmm_16, &gm20b_vmm_17, mmu, managed, addr, + size, argv, argc, key, name, pvmm); +} + +int +gm20b_vmm_new_fixed(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gf100_vmm_new_(&gm20b_vmm_16, &gm20b_vmm_17, mmu, managed, addr, + size, argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c new file mode 100644 index 000000000..17899fc95 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c @@ -0,0 +1,633 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include +#include +#include +#include +#include + +#include +#include + +static void +gp100_vmm_pfn_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + struct device *dev = vmm->mmu->subdev.device->dev; + dma_addr_t addr; + + nvkm_kmap(pt->memory); + while (ptes--) { + u32 datalo = nvkm_ro32(pt->memory, pt->base + ptei * 8 + 0); + u32 datahi = nvkm_ro32(pt->memory, pt->base + ptei * 8 + 4); + u64 data = (u64)datahi << 32 | datalo; + if ((data & (3ULL << 1)) != 0) { + addr = (data >> 8) << 12; + dma_unmap_page(dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + ptei++; + } + nvkm_done(pt->memory); +} + +static bool +gp100_vmm_pfn_clear(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + bool dma = false; + nvkm_kmap(pt->memory); + while (ptes--) { + u32 datalo = nvkm_ro32(pt->memory, pt->base + ptei * 8 + 0); + u32 datahi = nvkm_ro32(pt->memory, pt->base + ptei * 8 + 4); + u64 data = (u64)datahi << 32 | datalo; + if ((data & BIT_ULL(0)) && (data & (3ULL << 1)) != 0) { + VMM_WO064(pt, vmm, ptei * 8, data & ~BIT_ULL(0)); + dma = true; + } + ptei++; + } + nvkm_done(pt->memory); + return dma; +} + +static void +gp100_vmm_pgt_pfn(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + struct device *dev = vmm->mmu->subdev.device->dev; + dma_addr_t addr; + + nvkm_kmap(pt->memory); + for (; ptes; ptes--, map->pfn++) { + u64 data = 0; + + if (!(*map->pfn & NVKM_VMM_PFN_V)) + continue; + + if (!(*map->pfn & NVKM_VMM_PFN_W)) + data |= BIT_ULL(6); /* RO. */ + + if (!(*map->pfn & NVKM_VMM_PFN_A)) + data |= BIT_ULL(7); /* Atomic disable. */ + + if (!(*map->pfn & NVKM_VMM_PFN_VRAM)) { + addr = *map->pfn >> NVKM_VMM_PFN_ADDR_SHIFT; + addr = dma_map_page(dev, pfn_to_page(addr), 0, + PAGE_SIZE, DMA_BIDIRECTIONAL); + if (!WARN_ON(dma_mapping_error(dev, addr))) { + data |= addr >> 4; + data |= 2ULL << 1; /* SYSTEM_COHERENT_MEMORY. */ + data |= BIT_ULL(3); /* VOL. */ + data |= BIT_ULL(0); /* VALID. */ + } + } else { + data |= (*map->pfn & NVKM_VMM_PFN_ADDR) >> 4; + data |= BIT_ULL(0); /* VALID. */ + } + + VMM_WO064(pt, vmm, ptei++ * 8, data); + } + nvkm_done(pt->memory); +} + +static inline void +gp100_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr) +{ + u64 data = (addr >> 4) | map->type; + + map->type += ptes * map->ctag; + + while (ptes--) { + VMM_WO064(pt, vmm, ptei++ * 8, data); + data += map->next; + } +} + +static void +gp100_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, gp100_vmm_pgt_pte); +} + +static void +gp100_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + if (map->page->shift == PAGE_SHIFT) { + VMM_SPAM(vmm, "DMAA %08x %08x PTE(s)", ptei, ptes); + nvkm_kmap(pt->memory); + while (ptes--) { + const u64 data = (*map->dma++ >> 4) | map->type; + VMM_WO064(pt, vmm, ptei++ * 8, data); + map->type += map->ctag; + } + nvkm_done(pt->memory); + return; + } + + VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, gp100_vmm_pgt_pte); +} + +static void +gp100_vmm_pgt_mem(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_MEM(vmm, pt, ptei, ptes, map, gp100_vmm_pgt_pte); +} + +static void +gp100_vmm_pgt_sparse(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + /* VALID_FALSE + VOL tells the MMU to treat the PTE as sparse. */ + VMM_FO064(pt, vmm, ptei * 8, BIT_ULL(3) /* VOL. */, ptes); +} + +static const struct nvkm_vmm_desc_func +gp100_vmm_desc_spt = { + .unmap = gf100_vmm_pgt_unmap, + .sparse = gp100_vmm_pgt_sparse, + .mem = gp100_vmm_pgt_mem, + .dma = gp100_vmm_pgt_dma, + .sgl = gp100_vmm_pgt_sgl, + .pfn = gp100_vmm_pgt_pfn, + .pfn_clear = gp100_vmm_pfn_clear, + .pfn_unmap = gp100_vmm_pfn_unmap, +}; + +static void +gp100_vmm_lpt_invalid(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + /* VALID_FALSE + PRIV tells the MMU to ignore corresponding SPTEs. */ + VMM_FO064(pt, vmm, ptei * 8, BIT_ULL(5) /* PRIV. */, ptes); +} + +static const struct nvkm_vmm_desc_func +gp100_vmm_desc_lpt = { + .invalid = gp100_vmm_lpt_invalid, + .unmap = gf100_vmm_pgt_unmap, + .sparse = gp100_vmm_pgt_sparse, + .mem = gp100_vmm_pgt_mem, +}; + +static inline void +gp100_vmm_pd0_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr) +{ + u64 data = (addr >> 4) | map->type; + + map->type += ptes * map->ctag; + + while (ptes--) { + VMM_WO128(pt, vmm, ptei++ * 0x10, data, 0ULL); + data += map->next; + } +} + +static void +gp100_vmm_pd0_mem(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_MEM(vmm, pt, ptei, ptes, map, gp100_vmm_pd0_pte); +} + +static inline bool +gp100_vmm_pde(struct nvkm_mmu_pt *pt, u64 *data) +{ + switch (nvkm_memory_target(pt->memory)) { + case NVKM_MEM_TARGET_VRAM: *data |= 1ULL << 1; break; + case NVKM_MEM_TARGET_HOST: *data |= 2ULL << 1; + *data |= BIT_ULL(3); /* VOL. */ + break; + case NVKM_MEM_TARGET_NCOH: *data |= 3ULL << 1; break; + default: + WARN_ON(1); + return false; + } + *data |= pt->addr >> 4; + return true; +} + +static void +gp100_vmm_pd0_pde(struct nvkm_vmm *vmm, struct nvkm_vmm_pt *pgd, u32 pdei) +{ + struct nvkm_vmm_pt *pgt = pgd->pde[pdei]; + struct nvkm_mmu_pt *pd = pgd->pt[0]; + u64 data[2] = {}; + + if (pgt->pt[0] && !gp100_vmm_pde(pgt->pt[0], &data[0])) + return; + if (pgt->pt[1] && !gp100_vmm_pde(pgt->pt[1], &data[1])) + return; + + nvkm_kmap(pd->memory); + VMM_WO128(pd, vmm, pdei * 0x10, data[0], data[1]); + nvkm_done(pd->memory); +} + +static void +gp100_vmm_pd0_sparse(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 pdei, u32 pdes) +{ + /* VALID_FALSE + VOL_BIG tells the MMU to treat the PDE as sparse. */ + VMM_FO128(pt, vmm, pdei * 0x10, BIT_ULL(3) /* VOL_BIG. */, 0ULL, pdes); +} + +static void +gp100_vmm_pd0_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 pdei, u32 pdes) +{ + VMM_FO128(pt, vmm, pdei * 0x10, 0ULL, 0ULL, pdes); +} + +static void +gp100_vmm_pd0_pfn_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + struct device *dev = vmm->mmu->subdev.device->dev; + dma_addr_t addr; + + nvkm_kmap(pt->memory); + while (ptes--) { + u32 datalo = nvkm_ro32(pt->memory, pt->base + ptei * 16 + 0); + u32 datahi = nvkm_ro32(pt->memory, pt->base + ptei * 16 + 4); + u64 data = (u64)datahi << 32 | datalo; + + if ((data & (3ULL << 1)) != 0) { + addr = (data >> 8) << 12; + dma_unmap_page(dev, addr, 1UL << 21, DMA_BIDIRECTIONAL); + } + ptei++; + } + nvkm_done(pt->memory); +} + +static bool +gp100_vmm_pd0_pfn_clear(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + bool dma = false; + + nvkm_kmap(pt->memory); + while (ptes--) { + u32 datalo = nvkm_ro32(pt->memory, pt->base + ptei * 16 + 0); + u32 datahi = nvkm_ro32(pt->memory, pt->base + ptei * 16 + 4); + u64 data = (u64)datahi << 32 | datalo; + + if ((data & BIT_ULL(0)) && (data & (3ULL << 1)) != 0) { + VMM_WO064(pt, vmm, ptei * 16, data & ~BIT_ULL(0)); + dma = true; + } + ptei++; + } + nvkm_done(pt->memory); + return dma; +} + +static void +gp100_vmm_pd0_pfn(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + struct device *dev = vmm->mmu->subdev.device->dev; + dma_addr_t addr; + + nvkm_kmap(pt->memory); + for (; ptes; ptes--, map->pfn++) { + u64 data = 0; + + if (!(*map->pfn & NVKM_VMM_PFN_V)) + continue; + + if (!(*map->pfn & NVKM_VMM_PFN_W)) + data |= BIT_ULL(6); /* RO. */ + + if (!(*map->pfn & NVKM_VMM_PFN_A)) + data |= BIT_ULL(7); /* Atomic disable. */ + + if (!(*map->pfn & NVKM_VMM_PFN_VRAM)) { + addr = *map->pfn >> NVKM_VMM_PFN_ADDR_SHIFT; + addr = dma_map_page(dev, pfn_to_page(addr), 0, + 1UL << 21, DMA_BIDIRECTIONAL); + if (!WARN_ON(dma_mapping_error(dev, addr))) { + data |= addr >> 4; + data |= 2ULL << 1; /* SYSTEM_COHERENT_MEMORY. */ + data |= BIT_ULL(3); /* VOL. */ + data |= BIT_ULL(0); /* VALID. */ + } + } else { + data |= (*map->pfn & NVKM_VMM_PFN_ADDR) >> 4; + data |= BIT_ULL(0); /* VALID. */ + } + + VMM_WO064(pt, vmm, ptei++ * 16, data); + } + nvkm_done(pt->memory); +} + +static const struct nvkm_vmm_desc_func +gp100_vmm_desc_pd0 = { + .unmap = gp100_vmm_pd0_unmap, + .sparse = gp100_vmm_pd0_sparse, + .pde = gp100_vmm_pd0_pde, + .mem = gp100_vmm_pd0_mem, + .pfn = gp100_vmm_pd0_pfn, + .pfn_clear = gp100_vmm_pd0_pfn_clear, + .pfn_unmap = gp100_vmm_pd0_pfn_unmap, +}; + +static void +gp100_vmm_pd1_pde(struct nvkm_vmm *vmm, struct nvkm_vmm_pt *pgd, u32 pdei) +{ + struct nvkm_vmm_pt *pgt = pgd->pde[pdei]; + struct nvkm_mmu_pt *pd = pgd->pt[0]; + u64 data = 0; + + if (!gp100_vmm_pde(pgt->pt[0], &data)) + return; + + nvkm_kmap(pd->memory); + VMM_WO064(pd, vmm, pdei * 8, data); + nvkm_done(pd->memory); +} + +static const struct nvkm_vmm_desc_func +gp100_vmm_desc_pd1 = { + .unmap = gf100_vmm_pgt_unmap, + .sparse = gp100_vmm_pgt_sparse, + .pde = gp100_vmm_pd1_pde, +}; + +const struct nvkm_vmm_desc +gp100_vmm_desc_16[] = { + { LPT, 5, 8, 0x0100, &gp100_vmm_desc_lpt }, + { PGD, 8, 16, 0x1000, &gp100_vmm_desc_pd0 }, + { PGD, 9, 8, 0x1000, &gp100_vmm_desc_pd1 }, + { PGD, 9, 8, 0x1000, &gp100_vmm_desc_pd1 }, + { PGD, 2, 8, 0x1000, &gp100_vmm_desc_pd1 }, + {} +}; + +const struct nvkm_vmm_desc +gp100_vmm_desc_12[] = { + { SPT, 9, 8, 0x1000, &gp100_vmm_desc_spt }, + { PGD, 8, 16, 0x1000, &gp100_vmm_desc_pd0 }, + { PGD, 9, 8, 0x1000, &gp100_vmm_desc_pd1 }, + { PGD, 9, 8, 0x1000, &gp100_vmm_desc_pd1 }, + { PGD, 2, 8, 0x1000, &gp100_vmm_desc_pd1 }, + {} +}; + +int +gp100_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc, + struct nvkm_vmm_map *map) +{ + const enum nvkm_memory_target target = nvkm_memory_target(map->memory); + const struct nvkm_vmm_page *page = map->page; + union { + struct gp100_vmm_map_vn vn; + struct gp100_vmm_map_v0 v0; + } *args = argv; + struct nvkm_device *device = vmm->mmu->subdev.device; + struct nvkm_memory *memory = map->memory; + u8 kind, kind_inv, priv, ro, vol; + int kindn, aper, ret = -ENOSYS; + const u8 *kindm; + + map->next = (1ULL << page->shift) >> 4; + map->type = 0; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + vol = !!args->v0.vol; + ro = !!args->v0.ro; + priv = !!args->v0.priv; + kind = args->v0.kind; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + vol = target == NVKM_MEM_TARGET_HOST; + ro = 0; + priv = 0; + kind = 0x00; + } else { + VMM_DEBUG(vmm, "args"); + return ret; + } + + aper = vmm->func->aper(target); + if (WARN_ON(aper < 0)) + return aper; + + kindm = vmm->mmu->func->kind(vmm->mmu, &kindn, &kind_inv); + if (kind >= kindn || kindm[kind] == kind_inv) { + VMM_DEBUG(vmm, "kind %02x", kind); + return -EINVAL; + } + + if (kindm[kind] != kind) { + u64 tags = nvkm_memory_size(memory) >> 16; + if (aper != 0 || !(page->type & NVKM_VMM_PAGE_COMP)) { + VMM_DEBUG(vmm, "comp %d %02x", aper, page->type); + return -EINVAL; + } + + ret = nvkm_memory_tags_get(memory, device, tags, + nvkm_ltc_tags_clear, + &map->tags); + if (ret) { + VMM_DEBUG(vmm, "comp %d", ret); + return ret; + } + + if (map->tags->mn) { + tags = map->tags->mn->offset + (map->offset >> 16); + map->ctag |= ((1ULL << page->shift) >> 16) << 36; + map->type |= tags << 36; + map->next |= map->ctag; + } else { + kind = kindm[kind]; + } + } + + map->type |= BIT(0); + map->type |= (u64)aper << 1; + map->type |= (u64) vol << 3; + map->type |= (u64)priv << 5; + map->type |= (u64) ro << 6; + map->type |= (u64)kind << 56; + return 0; +} + +static int +gp100_vmm_fault_cancel(struct nvkm_vmm *vmm, void *argv, u32 argc) +{ + struct nvkm_device *device = vmm->mmu->subdev.device; + union { + struct gp100_vmm_fault_cancel_v0 v0; + } *args = argv; + int ret = -ENOSYS; + u32 aper; + + if ((ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) + return ret; + + /* Translate MaxwellFaultBufferA instance pointer to the same + * format as the NV_GR_FECS_CURRENT_CTX register. + */ + aper = (args->v0.inst >> 8) & 3; + args->v0.inst >>= 12; + args->v0.inst |= aper << 28; + args->v0.inst |= 0x80000000; + + if (!WARN_ON(nvkm_gr_ctxsw_pause(device))) { + if (nvkm_gr_ctxsw_inst(device) == args->v0.inst) { + gf100_vmm_invalidate(vmm, 0x0000001b + /* CANCEL_TARGETED. */ | + (args->v0.hub << 20) | + (args->v0.gpc << 15) | + (args->v0.client << 9)); + } + WARN_ON(nvkm_gr_ctxsw_resume(device)); + } + + return 0; +} + +static int +gp100_vmm_fault_replay(struct nvkm_vmm *vmm, void *argv, u32 argc) +{ + union { + struct gp100_vmm_fault_replay_vn vn; + } *args = argv; + int ret = -ENOSYS; + + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + gf100_vmm_invalidate(vmm, 0x0000000b); /* REPLAY_GLOBAL. */ + } + + return ret; +} + +int +gp100_vmm_mthd(struct nvkm_vmm *vmm, + struct nvkm_client *client, u32 mthd, void *argv, u32 argc) +{ + switch (mthd) { + case GP100_VMM_VN_FAULT_REPLAY: + return gp100_vmm_fault_replay(vmm, argv, argc); + case GP100_VMM_VN_FAULT_CANCEL: + return gp100_vmm_fault_cancel(vmm, argv, argc); + default: + break; + } + return -EINVAL; +} + +void +gp100_vmm_invalidate_pdb(struct nvkm_vmm *vmm, u64 addr) +{ + struct nvkm_device *device = vmm->mmu->subdev.device; + nvkm_wr32(device, 0x100cb8, lower_32_bits(addr)); + nvkm_wr32(device, 0x100cec, upper_32_bits(addr)); +} + +void +gp100_vmm_flush(struct nvkm_vmm *vmm, int depth) +{ + u32 type = (5 /* CACHE_LEVEL_UP_TO_PDE3 */ - depth) << 24; + if (atomic_read(&vmm->engref[NVKM_SUBDEV_BAR])) + type |= 0x00000004; /* HUB_ONLY */ + type |= 0x00000001; /* PAGE_ALL */ + gf100_vmm_invalidate(vmm, type); +} + +int +gp100_vmm_join(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + u64 base = BIT_ULL(10) /* VER2 */ | BIT_ULL(11) /* 64KiB */; + if (vmm->replay) { + base |= BIT_ULL(4); /* FAULT_REPLAY_TEX */ + base |= BIT_ULL(5); /* FAULT_REPLAY_GCC */ + } + return gf100_vmm_join_(vmm, inst, base); +} + +static const struct nvkm_vmm_func +gp100_vmm = { + .join = gp100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gp100_vmm_valid, + .flush = gp100_vmm_flush, + .mthd = gp100_vmm_mthd, + .invalidate_pdb = gp100_vmm_invalidate_pdb, + .page = { + { 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx }, + { 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx }, + { 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx }, + { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SVxC }, + { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SVxC }, + { 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SVHx }, + {} + } +}; + +int +gp100_vmm_new_(const struct nvkm_vmm_func *func, + struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + union { + struct gp100_vmm_vn vn; + struct gp100_vmm_v0 v0; + } *args = argv; + int ret = -ENOSYS; + bool replay; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + replay = args->v0.fault_replay != 0; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + replay = false; + } else + return ret; + + ret = nvkm_vmm_new_(func, mmu, 0, managed, addr, size, key, name, pvmm); + if (ret) + return ret; + + (*pvmm)->replay = replay; + return 0; +} + +int +gp100_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gp100_vmm_new_(&gp100_vmm, mmu, managed, addr, size, + argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c new file mode 100644 index 000000000..e081239af --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +static const struct nvkm_vmm_func +gp10b_vmm = { + .join = gp100_vmm_join, + .part = gf100_vmm_part, + .aper = gk20a_vmm_aper, + .valid = gp100_vmm_valid, + .flush = gp100_vmm_flush, + .mthd = gp100_vmm_mthd, + .invalidate_pdb = gp100_vmm_invalidate_pdb, + .page = { + { 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx }, + { 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx }, + { 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx }, + { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SxHC }, + { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SxHC }, + { 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SxHx }, + {} + } +}; + +int +gp10b_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gp100_vmm_new_(&gp10b_vmm, mmu, managed, addr, size, + argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgv100.c new file mode 100644 index 000000000..f0e21f632 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgv100.c @@ -0,0 +1,89 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include +#include + +#include +#include + +int +gv100_vmm_join(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + u64 data[2], mask; + int ret = gp100_vmm_join(vmm, inst), i; + if (ret) + return ret; + + nvkm_kmap(inst); + data[0] = nvkm_ro32(inst, 0x200); + data[1] = nvkm_ro32(inst, 0x204); + mask = BIT_ULL(0); + + nvkm_wo32(inst, 0x21c, 0x00000000); + + for (i = 0; i < 64; i++) { + if (mask & BIT_ULL(i)) { + nvkm_wo32(inst, 0x2a4 + (i * 0x10), data[1]); + nvkm_wo32(inst, 0x2a0 + (i * 0x10), data[0]); + } else { + nvkm_wo32(inst, 0x2a4 + (i * 0x10), 0x00000001); + nvkm_wo32(inst, 0x2a0 + (i * 0x10), 0x00000001); + } + nvkm_wo32(inst, 0x2a8 + (i * 0x10), 0x00000000); + } + + nvkm_wo32(inst, 0x298, lower_32_bits(mask)); + nvkm_wo32(inst, 0x29c, upper_32_bits(mask)); + nvkm_done(inst); + return 0; +} + +static const struct nvkm_vmm_func +gv100_vmm = { + .join = gv100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gp100_vmm_valid, + .flush = gp100_vmm_flush, + .mthd = gp100_vmm_mthd, + .invalidate_pdb = gp100_vmm_invalidate_pdb, + .page = { + { 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx }, + { 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx }, + { 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx }, + { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SVxC }, + { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SVxC }, + { 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SVHx }, + {} + } +}; + +int +gv100_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gp100_vmm_new_(&gv100_vmm, mmu, managed, addr, size, + argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmmcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmmcp77.c new file mode 100644 index 000000000..bdddd99f5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmmcp77.c @@ -0,0 +1,45 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +static const struct nvkm_vmm_func +mcp77_vmm = { + .join = nv50_vmm_join, + .part = nv50_vmm_part, + .valid = nv50_vmm_valid, + .flush = nv50_vmm_flush, + .page_block = 1 << 29, + .page = { + { 16, &nv50_vmm_desc_16[0], NVKM_VMM_PAGE_xVxx }, + { 12, &nv50_vmm_desc_12[0], NVKM_VMM_PAGE_xVHx }, + {} + } +}; + +int +mcp77_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return nv04_vmm_new_(&mcp77_vmm, mmu, 0, managed, addr, size, + argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv04.c new file mode 100644 index 000000000..4c6b3b7d2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv04.c @@ -0,0 +1,141 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include +#include + +static inline void +nv04_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr) +{ + u32 data = addr | 0x00000003; /* PRESENT, RW. */ + while (ptes--) { + VMM_WO032(pt, vmm, 8 + ptei++ * 4, data); + data += 0x00001000; + } +} + +static void +nv04_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, nv04_vmm_pgt_pte); +} + +static void +nv04_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ +#if PAGE_SHIFT == 12 + nvkm_kmap(pt->memory); + while (ptes--) + VMM_WO032(pt, vmm, 8 + (ptei++ * 4), *map->dma++ | 0x00000003); + nvkm_done(pt->memory); +#else + VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, nv04_vmm_pgt_pte); +#endif +} + +static void +nv04_vmm_pgt_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + VMM_FO032(pt, vmm, 8 + (ptei * 4), 0, ptes); +} + +static const struct nvkm_vmm_desc_func +nv04_vmm_desc_pgt = { + .unmap = nv04_vmm_pgt_unmap, + .dma = nv04_vmm_pgt_dma, + .sgl = nv04_vmm_pgt_sgl, +}; + +static const struct nvkm_vmm_desc +nv04_vmm_desc_12[] = { + { PGT, 15, 4, 0x1000, &nv04_vmm_desc_pgt }, + {} +}; + +int +nv04_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc, + struct nvkm_vmm_map *map) +{ + union { + struct nv04_vmm_map_vn vn; + } *args = argv; + int ret = -ENOSYS; + if ((ret = nvif_unvers(ret, &argv, &argc, args->vn))) + VMM_DEBUG(vmm, "args"); + return ret; +} + +static const struct nvkm_vmm_func +nv04_vmm = { + .valid = nv04_vmm_valid, + .page = { + { 12, &nv04_vmm_desc_12[0], NVKM_VMM_PAGE_HOST }, + {} + } +}; + +int +nv04_vmm_new_(const struct nvkm_vmm_func *func, struct nvkm_mmu *mmu, + u32 pd_header, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + union { + struct nv04_vmm_vn vn; + } *args = argv; + int ret; + + ret = nvkm_vmm_new_(func, mmu, pd_header, managed, addr, size, + key, name, pvmm); + if (ret) + return ret; + + return nvif_unvers(-ENOSYS, &argv, &argc, args->vn); +} + +int +nv04_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, const char *name, + struct nvkm_vmm **pvmm) +{ + struct nvkm_memory *mem; + struct nvkm_vmm *vmm; + int ret; + + ret = nv04_vmm_new_(&nv04_vmm, mmu, 8, managed, addr, size, + argv, argc, key, name, &vmm); + *pvmm = vmm; + if (ret) + return ret; + + mem = vmm->pd->pt[0]->memory; + nvkm_kmap(mem); + nvkm_wo32(mem, 0x00000, 0x0002103d); /* PCI, RW, PT, !LN */ + nvkm_wo32(mem, 0x00004, vmm->limit - 1); + nvkm_done(mem); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv41.c new file mode 100644 index 000000000..31984671d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv41.c @@ -0,0 +1,112 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include + +static void +nv41_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr) +{ + u32 data = (addr >> 7) | 0x00000001; /* VALID. */ + while (ptes--) { + VMM_WO032(pt, vmm, ptei++ * 4, data); + data += 0x00000020; + } +} + +static void +nv41_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, nv41_vmm_pgt_pte); +} + +static void +nv41_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ +#if PAGE_SHIFT == 12 + nvkm_kmap(pt->memory); + while (ptes--) { + const u32 data = (*map->dma++ >> 7) | 0x00000001; + VMM_WO032(pt, vmm, ptei++ * 4, data); + } + nvkm_done(pt->memory); +#else + VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, nv41_vmm_pgt_pte); +#endif +} + +static void +nv41_vmm_pgt_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + VMM_FO032(pt, vmm, ptei * 4, 0, ptes); +} + +static const struct nvkm_vmm_desc_func +nv41_vmm_desc_pgt = { + .unmap = nv41_vmm_pgt_unmap, + .dma = nv41_vmm_pgt_dma, + .sgl = nv41_vmm_pgt_sgl, +}; + +static const struct nvkm_vmm_desc +nv41_vmm_desc_12[] = { + { PGT, 17, 4, 0x1000, &nv41_vmm_desc_pgt }, + {} +}; + +static void +nv41_vmm_flush(struct nvkm_vmm *vmm, int level) +{ + struct nvkm_device *device = vmm->mmu->subdev.device; + + mutex_lock(&vmm->mmu->mutex); + nvkm_wr32(device, 0x100810, 0x00000022); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x100810) & 0x00000020) + break; + ); + nvkm_wr32(device, 0x100810, 0x00000000); + mutex_unlock(&vmm->mmu->mutex); +} + +static const struct nvkm_vmm_func +nv41_vmm = { + .valid = nv04_vmm_valid, + .flush = nv41_vmm_flush, + .page = { + { 12, &nv41_vmm_desc_12[0], NVKM_VMM_PAGE_HOST }, + {} + } +}; + +int +nv41_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, const char *name, + struct nvkm_vmm **pvmm) +{ + return nv04_vmm_new_(&nv41_vmm, mmu, 0, managed, addr, size, + argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c new file mode 100644 index 000000000..a82936ba9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c @@ -0,0 +1,230 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include + +static void +nv44_vmm_pgt_fill(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + dma_addr_t *list, u32 ptei, u32 ptes) +{ + u32 pteo = (ptei << 2) & ~0x0000000f; + u32 tmp[4]; + + tmp[0] = nvkm_ro32(pt->memory, pteo + 0x0); + tmp[1] = nvkm_ro32(pt->memory, pteo + 0x4); + tmp[2] = nvkm_ro32(pt->memory, pteo + 0x8); + tmp[3] = nvkm_ro32(pt->memory, pteo + 0xc); + + while (ptes--) { + u32 addr = (list ? *list++ : vmm->null) >> 12; + switch (ptei++ & 0x3) { + case 0: + tmp[0] &= ~0x07ffffff; + tmp[0] |= addr; + break; + case 1: + tmp[0] &= ~0xf8000000; + tmp[0] |= addr << 27; + tmp[1] &= ~0x003fffff; + tmp[1] |= addr >> 5; + break; + case 2: + tmp[1] &= ~0xffc00000; + tmp[1] |= addr << 22; + tmp[2] &= ~0x0001ffff; + tmp[2] |= addr >> 10; + break; + case 3: + tmp[2] &= ~0xfffe0000; + tmp[2] |= addr << 17; + tmp[3] &= ~0x00000fff; + tmp[3] |= addr >> 15; + break; + } + } + + VMM_WO032(pt, vmm, pteo + 0x0, tmp[0]); + VMM_WO032(pt, vmm, pteo + 0x4, tmp[1]); + VMM_WO032(pt, vmm, pteo + 0x8, tmp[2]); + VMM_WO032(pt, vmm, pteo + 0xc, tmp[3] | 0x40000000); +} + +static void +nv44_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr) +{ + dma_addr_t tmp[4], i; + + if (ptei & 3) { + const u32 pten = min(ptes, 4 - (ptei & 3)); + for (i = 0; i < pten; i++, addr += 0x1000) + tmp[i] = addr; + nv44_vmm_pgt_fill(vmm, pt, tmp, ptei, pten); + ptei += pten; + ptes -= pten; + } + + while (ptes >= 4) { + for (i = 0; i < 4; i++, addr += 0x1000) + tmp[i] = addr >> 12; + VMM_WO032(pt, vmm, ptei++ * 4, tmp[0] >> 0 | tmp[1] << 27); + VMM_WO032(pt, vmm, ptei++ * 4, tmp[1] >> 5 | tmp[2] << 22); + VMM_WO032(pt, vmm, ptei++ * 4, tmp[2] >> 10 | tmp[3] << 17); + VMM_WO032(pt, vmm, ptei++ * 4, tmp[3] >> 15 | 0x40000000); + ptes -= 4; + } + + if (ptes) { + for (i = 0; i < ptes; i++, addr += 0x1000) + tmp[i] = addr; + nv44_vmm_pgt_fill(vmm, pt, tmp, ptei, ptes); + } +} + +static void +nv44_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, nv44_vmm_pgt_pte); +} + +static void +nv44_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ +#if PAGE_SHIFT == 12 + nvkm_kmap(pt->memory); + if (ptei & 3) { + const u32 pten = min(ptes, 4 - (ptei & 3)); + nv44_vmm_pgt_fill(vmm, pt, map->dma, ptei, pten); + ptei += pten; + ptes -= pten; + map->dma += pten; + } + + while (ptes >= 4) { + u32 tmp[4], i; + for (i = 0; i < 4; i++) + tmp[i] = *map->dma++ >> 12; + VMM_WO032(pt, vmm, ptei++ * 4, tmp[0] >> 0 | tmp[1] << 27); + VMM_WO032(pt, vmm, ptei++ * 4, tmp[1] >> 5 | tmp[2] << 22); + VMM_WO032(pt, vmm, ptei++ * 4, tmp[2] >> 10 | tmp[3] << 17); + VMM_WO032(pt, vmm, ptei++ * 4, tmp[3] >> 15 | 0x40000000); + ptes -= 4; + } + + if (ptes) { + nv44_vmm_pgt_fill(vmm, pt, map->dma, ptei, ptes); + map->dma += ptes; + } + nvkm_done(pt->memory); +#else + VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, nv44_vmm_pgt_pte); +#endif +} + +static void +nv44_vmm_pgt_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + nvkm_kmap(pt->memory); + if (ptei & 3) { + const u32 pten = min(ptes, 4 - (ptei & 3)); + nv44_vmm_pgt_fill(vmm, pt, NULL, ptei, pten); + ptei += pten; + ptes -= pten; + } + + while (ptes > 4) { + VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000); + VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000); + VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000); + VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000); + ptes -= 4; + } + + if (ptes) + nv44_vmm_pgt_fill(vmm, pt, NULL, ptei, ptes); + nvkm_done(pt->memory); +} + +static const struct nvkm_vmm_desc_func +nv44_vmm_desc_pgt = { + .unmap = nv44_vmm_pgt_unmap, + .dma = nv44_vmm_pgt_dma, + .sgl = nv44_vmm_pgt_sgl, +}; + +static const struct nvkm_vmm_desc +nv44_vmm_desc_12[] = { + { PGT, 17, 4, 0x80000, &nv44_vmm_desc_pgt }, + {} +}; + +static void +nv44_vmm_flush(struct nvkm_vmm *vmm, int level) +{ + struct nvkm_device *device = vmm->mmu->subdev.device; + nvkm_wr32(device, 0x100814, vmm->limit - 4096); + nvkm_wr32(device, 0x100808, 0x000000020); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x100808) & 0x00000001) + break; + ); + nvkm_wr32(device, 0x100808, 0x00000000); +} + +static const struct nvkm_vmm_func +nv44_vmm = { + .valid = nv04_vmm_valid, + .flush = nv44_vmm_flush, + .page = { + { 12, &nv44_vmm_desc_12[0], NVKM_VMM_PAGE_HOST }, + {} + } +}; + +int +nv44_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, const char *name, + struct nvkm_vmm **pvmm) +{ + struct nvkm_subdev *subdev = &mmu->subdev; + struct nvkm_vmm *vmm; + int ret; + + ret = nv04_vmm_new_(&nv44_vmm, mmu, 0, managed, addr, size, + argv, argc, key, name, &vmm); + *pvmm = vmm; + if (ret) + return ret; + + vmm->nullp = dma_alloc_coherent(subdev->device->dev, 16 * 1024, + &vmm->null, GFP_KERNEL); + if (!vmm->nullp) { + nvkm_warn(subdev, "unable to allocate dummy pages\n"); + vmm->null = 0; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv50.c new file mode 100644 index 000000000..b7548dcd7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv50.c @@ -0,0 +1,384 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include +#include +#include + +#include +#include + +static inline void +nv50_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr) +{ + u64 next = addr + map->type, data; + u32 pten; + int log2blk; + + map->type += ptes * map->ctag; + + while (ptes) { + for (log2blk = 7; log2blk >= 0; log2blk--) { + pten = 1 << log2blk; + if (ptes >= pten && IS_ALIGNED(ptei, pten)) + break; + } + + data = next | (log2blk << 7); + next += pten * map->next; + ptes -= pten; + + while (pten--) + VMM_WO064(pt, vmm, ptei++ * 8, data); + } +} + +static void +nv50_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, nv50_vmm_pgt_pte); +} + +static void +nv50_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + if (map->page->shift == PAGE_SHIFT) { + VMM_SPAM(vmm, "DMAA %08x %08x PTE(s)", ptei, ptes); + nvkm_kmap(pt->memory); + while (ptes--) { + const u64 data = *map->dma++ + map->type; + VMM_WO064(pt, vmm, ptei++ * 8, data); + map->type += map->ctag; + } + nvkm_done(pt->memory); + return; + } + + VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, nv50_vmm_pgt_pte); +} + +static void +nv50_vmm_pgt_mem(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt, + u32 ptei, u32 ptes, struct nvkm_vmm_map *map) +{ + VMM_MAP_ITER_MEM(vmm, pt, ptei, ptes, map, nv50_vmm_pgt_pte); +} + +static void +nv50_vmm_pgt_unmap(struct nvkm_vmm *vmm, + struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes) +{ + VMM_FO064(pt, vmm, ptei * 8, 0ULL, ptes); +} + +static const struct nvkm_vmm_desc_func +nv50_vmm_pgt = { + .unmap = nv50_vmm_pgt_unmap, + .mem = nv50_vmm_pgt_mem, + .dma = nv50_vmm_pgt_dma, + .sgl = nv50_vmm_pgt_sgl, +}; + +static bool +nv50_vmm_pde(struct nvkm_vmm *vmm, struct nvkm_vmm_pt *pgt, u64 *pdata) +{ + struct nvkm_mmu_pt *pt; + u64 data = 0xdeadcafe00000000ULL; + if (pgt && (pt = pgt->pt[0])) { + switch (pgt->page) { + case 16: data = 0x00000001; break; + case 12: data = 0x00000003; + switch (nvkm_memory_size(pt->memory)) { + case 0x100000: data |= 0x00000000; break; + case 0x040000: data |= 0x00000020; break; + case 0x020000: data |= 0x00000040; break; + case 0x010000: data |= 0x00000060; break; + default: + WARN_ON(1); + return false; + } + break; + default: + WARN_ON(1); + return false; + } + + switch (nvkm_memory_target(pt->memory)) { + case NVKM_MEM_TARGET_VRAM: data |= 0x00000000; break; + case NVKM_MEM_TARGET_HOST: data |= 0x00000008; break; + case NVKM_MEM_TARGET_NCOH: data |= 0x0000000c; break; + default: + WARN_ON(1); + return false; + } + + data |= pt->addr; + } + *pdata = data; + return true; +} + +static void +nv50_vmm_pgd_pde(struct nvkm_vmm *vmm, struct nvkm_vmm_pt *pgd, u32 pdei) +{ + struct nvkm_vmm_join *join; + u32 pdeo = vmm->mmu->func->vmm.pd_offset + (pdei * 8); + u64 data; + + if (!nv50_vmm_pde(vmm, pgd->pde[pdei], &data)) + return; + + list_for_each_entry(join, &vmm->join, head) { + nvkm_kmap(join->inst); + nvkm_wo64(join->inst, pdeo, data); + nvkm_done(join->inst); + } +} + +static const struct nvkm_vmm_desc_func +nv50_vmm_pgd = { + .pde = nv50_vmm_pgd_pde, +}; + +const struct nvkm_vmm_desc +nv50_vmm_desc_12[] = { + { PGT, 17, 8, 0x1000, &nv50_vmm_pgt }, + { PGD, 11, 0, 0x0000, &nv50_vmm_pgd }, + {} +}; + +const struct nvkm_vmm_desc +nv50_vmm_desc_16[] = { + { PGT, 13, 8, 0x1000, &nv50_vmm_pgt }, + { PGD, 11, 0, 0x0000, &nv50_vmm_pgd }, + {} +}; + +void +nv50_vmm_flush(struct nvkm_vmm *vmm, int level) +{ + struct nvkm_subdev *subdev = &vmm->mmu->subdev; + struct nvkm_device *device = subdev->device; + int i, id; + + mutex_lock(&vmm->mmu->mutex); + for (i = 0; i < NVKM_SUBDEV_NR; i++) { + if (!atomic_read(&vmm->engref[i])) + continue; + + /* unfortunate hw bug workaround... */ + if (i == NVKM_ENGINE_GR && device->gr) { + int ret = nvkm_gr_tlb_flush(device->gr); + if (ret != -ENODEV) + continue; + } + + switch (i) { + case NVKM_ENGINE_GR : id = 0x00; break; + case NVKM_ENGINE_VP : + case NVKM_ENGINE_MSPDEC: id = 0x01; break; + case NVKM_SUBDEV_BAR : id = 0x06; break; + case NVKM_ENGINE_MSPPP : + case NVKM_ENGINE_MPEG : id = 0x08; break; + case NVKM_ENGINE_BSP : + case NVKM_ENGINE_MSVLD : id = 0x09; break; + case NVKM_ENGINE_CIPHER: + case NVKM_ENGINE_SEC : id = 0x0a; break; + case NVKM_ENGINE_CE : id = 0x0d; break; + default: + continue; + } + + nvkm_wr32(device, 0x100c80, (id << 16) | 1); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x100c80) & 0x00000001)) + break; + ) < 0) + nvkm_error(subdev, "%s mmu invalidate timeout\n", nvkm_subdev_type[i]); + } + mutex_unlock(&vmm->mmu->mutex); +} + +int +nv50_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc, + struct nvkm_vmm_map *map) +{ + const struct nvkm_vmm_page *page = map->page; + union { + struct nv50_vmm_map_vn vn; + struct nv50_vmm_map_v0 v0; + } *args = argv; + struct nvkm_device *device = vmm->mmu->subdev.device; + struct nvkm_ram *ram = device->fb->ram; + struct nvkm_memory *memory = map->memory; + u8 aper, kind, kind_inv, comp, priv, ro; + int kindn, ret = -ENOSYS; + const u8 *kindm; + + map->type = map->ctag = 0; + map->next = 1 << page->shift; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + ro = !!args->v0.ro; + priv = !!args->v0.priv; + kind = args->v0.kind & 0x7f; + comp = args->v0.comp & 0x03; + } else + if (!(ret = nvif_unvers(ret, &argv, &argc, args->vn))) { + ro = 0; + priv = 0; + kind = 0x00; + comp = 0; + } else { + VMM_DEBUG(vmm, "args"); + return ret; + } + + switch (nvkm_memory_target(memory)) { + case NVKM_MEM_TARGET_VRAM: + if (ram->stolen) { + map->type |= ram->stolen; + aper = 3; + } else { + aper = 0; + } + break; + case NVKM_MEM_TARGET_HOST: + aper = 2; + break; + case NVKM_MEM_TARGET_NCOH: + aper = 3; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + kindm = vmm->mmu->func->kind(vmm->mmu, &kindn, &kind_inv); + if (kind >= kindn || kindm[kind] == kind_inv) { + VMM_DEBUG(vmm, "kind %02x", kind); + return -EINVAL; + } + + if (map->mem && map->mem->type != kindm[kind]) { + VMM_DEBUG(vmm, "kind %02x bankswz: %d %d", kind, + kindm[kind], map->mem->type); + return -EINVAL; + } + + if (comp) { + u32 tags = (nvkm_memory_size(memory) >> 16) * comp; + if (aper != 0 || !(page->type & NVKM_VMM_PAGE_COMP)) { + VMM_DEBUG(vmm, "comp %d %02x", aper, page->type); + return -EINVAL; + } + + ret = nvkm_memory_tags_get(memory, device, tags, NULL, + &map->tags); + if (ret) { + VMM_DEBUG(vmm, "comp %d", ret); + return ret; + } + + if (map->tags->mn) { + u32 tags = map->tags->mn->offset + (map->offset >> 16); + map->ctag |= (u64)comp << 49; + map->type |= (u64)comp << 47; + map->type |= (u64)tags << 49; + map->next |= map->ctag; + } + } + + map->type |= BIT(0); /* Valid. */ + map->type |= (u64)ro << 3; + map->type |= (u64)aper << 4; + map->type |= (u64)priv << 6; + map->type |= (u64)kind << 40; + return 0; +} + +void +nv50_vmm_part(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + struct nvkm_vmm_join *join; + + list_for_each_entry(join, &vmm->join, head) { + if (join->inst == inst) { + list_del(&join->head); + kfree(join); + break; + } + } +} + +int +nv50_vmm_join(struct nvkm_vmm *vmm, struct nvkm_memory *inst) +{ + const u32 pd_offset = vmm->mmu->func->vmm.pd_offset; + struct nvkm_vmm_join *join; + int ret = 0; + u64 data; + u32 pdei; + + if (!(join = kmalloc(sizeof(*join), GFP_KERNEL))) + return -ENOMEM; + join->inst = inst; + list_add_tail(&join->head, &vmm->join); + + nvkm_kmap(join->inst); + for (pdei = vmm->start >> 29; pdei <= (vmm->limit - 1) >> 29; pdei++) { + if (!nv50_vmm_pde(vmm, vmm->pd->pde[pdei], &data)) { + ret = -EINVAL; + break; + } + nvkm_wo64(join->inst, pd_offset + (pdei * 8), data); + } + nvkm_done(join->inst); + return ret; +} + +static const struct nvkm_vmm_func +nv50_vmm = { + .join = nv50_vmm_join, + .part = nv50_vmm_part, + .valid = nv50_vmm_valid, + .flush = nv50_vmm_flush, + .page_block = 1 << 29, + .page = { + { 16, &nv50_vmm_desc_16[0], NVKM_VMM_PAGE_xVxC }, + { 12, &nv50_vmm_desc_12[0], NVKM_VMM_PAGE_xVHx }, + {} + } +}; + +int +nv50_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, const char *name, + struct nvkm_vmm **pvmm) +{ + return nv04_vmm_new_(&nv50_vmm, mmu, 0, managed, addr, size, + argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c new file mode 100644 index 000000000..5a08458fe --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "vmm.h" + +#include + +static void +tu102_vmm_flush(struct nvkm_vmm *vmm, int depth) +{ + struct nvkm_device *device = vmm->mmu->subdev.device; + u32 type = (5 /* CACHE_LEVEL_UP_TO_PDE3 */ - depth) << 24; + + type |= 0x00000001; /* PAGE_ALL */ + if (atomic_read(&vmm->engref[NVKM_SUBDEV_BAR])) + type |= 0x00000006; /* HUB_ONLY | ALL PDB (hack) */ + + mutex_lock(&vmm->mmu->mutex); + + nvkm_wr32(device, 0xb830a0, vmm->pd->pt[0]->addr >> 8); + nvkm_wr32(device, 0xb830a4, 0x00000000); + nvkm_wr32(device, 0x100e68, 0x00000000); + nvkm_wr32(device, 0xb830b0, 0x80000000 | type); + + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0xb830b0) & 0x80000000)) + break; + ); + + mutex_unlock(&vmm->mmu->mutex); +} + +static const struct nvkm_vmm_func +tu102_vmm = { + .join = gv100_vmm_join, + .part = gf100_vmm_part, + .aper = gf100_vmm_aper, + .valid = gp100_vmm_valid, + .flush = tu102_vmm_flush, + .mthd = gp100_vmm_mthd, + .page = { + { 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx }, + { 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx }, + { 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx }, + { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SVxC }, + { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SVxC }, + { 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SVHx }, + {} + } +}; + +int +tu102_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size, + void *argv, u32 argc, struct lock_class_key *key, + const char *name, struct nvkm_vmm **pvmm) +{ + return gp100_vmm_new_(&tu102_vmm, mmu, managed, addr, size, + argv, argc, key, name, pvmm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild new file mode 100644 index 000000000..5124a0c41 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/mxm/base.o +nvkm-y += nvkm/subdev/mxm/mxms.o +nvkm-y += nvkm/subdev/mxm/nv50.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c new file mode 100644 index 000000000..c1acfe642 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c @@ -0,0 +1,279 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "mxms.h" + +#include +#include +#include +#include + +static bool +mxm_shadow_rom_fetch(struct nvkm_i2c_bus *bus, u8 addr, + u8 offset, u8 size, u8 *data) +{ + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 1, .buf = &offset }, + { .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, }, + }; + + return i2c_transfer(&bus->i2c, msgs, 2) == 2; +} + +static bool +mxm_shadow_rom(struct nvkm_mxm *mxm, u8 version) +{ + struct nvkm_device *device = mxm->subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvkm_i2c *i2c = device->i2c; + struct nvkm_i2c_bus *bus = NULL; + u8 i2cidx, mxms[6], addr, size; + + i2cidx = mxm_ddc_map(bios, 1 /* LVDS_DDC */) & 0x0f; + if (i2cidx < 0x0f) + bus = nvkm_i2c_bus_find(i2c, i2cidx); + if (!bus) + return false; + + addr = 0x54; + if (!mxm_shadow_rom_fetch(bus, addr, 0, 6, mxms)) { + addr = 0x56; + if (!mxm_shadow_rom_fetch(bus, addr, 0, 6, mxms)) + return false; + } + + mxm->mxms = mxms; + size = mxms_headerlen(mxm) + mxms_structlen(mxm); + mxm->mxms = kmalloc(size, GFP_KERNEL); + + if (mxm->mxms && + mxm_shadow_rom_fetch(bus, addr, 0, size, mxm->mxms)) + return true; + + kfree(mxm->mxms); + mxm->mxms = NULL; + return false; +} + +#if defined(CONFIG_ACPI) +static bool +mxm_shadow_dsm(struct nvkm_mxm *mxm, u8 version) +{ + struct nvkm_subdev *subdev = &mxm->subdev; + struct nvkm_device *device = subdev->device; + static guid_t muid = + GUID_INIT(0x4004A400, 0x917D, 0x4CF2, + 0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65); + u32 mxms_args[] = { 0x00000000 }; + union acpi_object argv4 = { + .buffer.type = ACPI_TYPE_BUFFER, + .buffer.length = sizeof(mxms_args), + .buffer.pointer = (char *)mxms_args, + }; + union acpi_object *obj; + acpi_handle handle; + int rev; + + handle = ACPI_HANDLE(device->dev); + if (!handle) + return false; + + /* + * spec says this can be zero to mean "highest revision", but + * of course there's at least one bios out there which fails + * unless you pass in exactly the version it supports.. + */ + rev = (version & 0xf0) << 4 | (version & 0x0f); + obj = acpi_evaluate_dsm(handle, &muid, rev, 0x00000010, &argv4); + if (!obj) { + nvkm_debug(subdev, "DSM MXMS failed\n"); + return false; + } + + if (obj->type == ACPI_TYPE_BUFFER) { + mxm->mxms = kmemdup(obj->buffer.pointer, + obj->buffer.length, GFP_KERNEL); + } else if (obj->type == ACPI_TYPE_INTEGER) { + nvkm_debug(subdev, "DSM MXMS returned 0x%llx\n", + obj->integer.value); + } + + ACPI_FREE(obj); + return mxm->mxms != NULL; +} +#endif + +#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) + +#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" + +static u8 +wmi_wmmx_mxmi(struct nvkm_mxm *mxm, u8 version) +{ + struct nvkm_subdev *subdev = &mxm->subdev; + u32 mxmi_args[] = { 0x494D584D /* MXMI */, version, 0 }; + struct acpi_buffer args = { sizeof(mxmi_args), mxmi_args }; + struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); + if (ACPI_FAILURE(status)) { + nvkm_debug(subdev, "WMMX MXMI returned %d\n", status); + return 0x00; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_INTEGER) { + version = obj->integer.value; + nvkm_debug(subdev, "WMMX MXMI version %d.%d\n", + (version >> 4), version & 0x0f); + } else { + version = 0; + nvkm_debug(subdev, "WMMX MXMI returned non-integer\n"); + } + + kfree(obj); + return version; +} + +static bool +mxm_shadow_wmi(struct nvkm_mxm *mxm, u8 version) +{ + struct nvkm_subdev *subdev = &mxm->subdev; + u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 }; + struct acpi_buffer args = { sizeof(mxms_args), mxms_args }; + struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + if (!wmi_has_guid(WMI_WMMX_GUID)) { + nvkm_debug(subdev, "WMMX GUID not found\n"); + return false; + } + + mxms_args[1] = wmi_wmmx_mxmi(mxm, 0x00); + if (!mxms_args[1]) + mxms_args[1] = wmi_wmmx_mxmi(mxm, version); + if (!mxms_args[1]) + return false; + + status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); + if (ACPI_FAILURE(status)) { + nvkm_debug(subdev, "WMMX MXMS returned %d\n", status); + return false; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_BUFFER) { + mxm->mxms = kmemdup(obj->buffer.pointer, + obj->buffer.length, GFP_KERNEL); + } + + kfree(obj); + return mxm->mxms != NULL; +} +#endif + +static struct mxm_shadow_h { + const char *name; + bool (*exec)(struct nvkm_mxm *, u8 version); +} _mxm_shadow[] = { + { "ROM", mxm_shadow_rom }, +#if defined(CONFIG_ACPI) + { "DSM", mxm_shadow_dsm }, +#endif +#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) + { "WMI", mxm_shadow_wmi }, +#endif + {} +}; + +static int +mxm_shadow(struct nvkm_mxm *mxm, u8 version) +{ + struct mxm_shadow_h *shadow = _mxm_shadow; + do { + nvkm_debug(&mxm->subdev, "checking %s\n", shadow->name); + if (shadow->exec(mxm, version)) { + if (mxms_valid(mxm)) + return 0; + kfree(mxm->mxms); + mxm->mxms = NULL; + } + } while ((++shadow)->name); + return -ENOENT; +} + +static const struct nvkm_subdev_func +nvkm_mxm = { +}; + +int +nvkm_mxm_new_(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_mxm **pmxm) +{ + struct nvkm_bios *bios = device->bios; + struct nvkm_mxm *mxm; + u8 ver, len; + u16 data; + + if (!(mxm = *pmxm = kzalloc(sizeof(*mxm), GFP_KERNEL))) + return -ENOMEM; + + nvkm_subdev_ctor(&nvkm_mxm, device, type, inst, &mxm->subdev); + + data = mxm_table(bios, &ver, &len); + if (!data || !(ver = nvbios_rd08(bios, data))) { + nvkm_debug(&mxm->subdev, "no VBIOS data, nothing to do\n"); + return 0; + } + + nvkm_info(&mxm->subdev, "BIOS version %d.%d\n", ver >> 4, ver & 0x0f); + nvkm_debug(&mxm->subdev, "module flags: %02x\n", + nvbios_rd08(bios, data + 0x01)); + nvkm_debug(&mxm->subdev, "config flags: %02x\n", + nvbios_rd08(bios, data + 0x02)); + + if (mxm_shadow(mxm, ver)) { + nvkm_warn(&mxm->subdev, "failed to locate valid SIS\n"); +#if 0 + /* we should, perhaps, fall back to some kind of limited + * mode here if the x86 vbios hasn't already done the + * work for us (so we prevent loading with completely + * whacked vbios tables). + */ + return -EINVAL; +#else + return 0; +#endif + } + + nvkm_debug(&mxm->subdev, "MXMS Version %d.%d\n", + mxms_version(mxm) >> 8, mxms_version(mxm) & 0xff); + mxms_foreach(mxm, 0, NULL, NULL); + + if (nvkm_boolopt(device->cfgopt, "NvMXMDCB", true)) + mxm->action |= MXM_SANITISE_DCB; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c new file mode 100644 index 000000000..9abfa5e2f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c @@ -0,0 +1,191 @@ +/* + * 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 "mxms.h" + +#define ROM16(x) get_unaligned_le16(&(x)) +#define ROM32(x) get_unaligned_le32(&(x)) + +static u8 * +mxms_data(struct nvkm_mxm *mxm) +{ + return mxm->mxms; + +} + +u16 +mxms_version(struct nvkm_mxm *mxm) +{ + u8 *mxms = mxms_data(mxm); + u16 version = (mxms[4] << 8) | mxms[5]; + switch (version ) { + case 0x0200: + case 0x0201: + case 0x0300: + return version; + default: + break; + } + + nvkm_debug(&mxm->subdev, "unknown version %d.%d\n", mxms[4], mxms[5]); + return 0x0000; +} + +u16 +mxms_headerlen(struct nvkm_mxm *mxm) +{ + return 8; +} + +u16 +mxms_structlen(struct nvkm_mxm *mxm) +{ + return *(u16 *)&mxms_data(mxm)[6]; +} + +bool +mxms_checksum(struct nvkm_mxm *mxm) +{ + u16 size = mxms_headerlen(mxm) + mxms_structlen(mxm); + u8 *mxms = mxms_data(mxm), sum = 0; + while (size--) + sum += *mxms++; + if (sum) { + nvkm_debug(&mxm->subdev, "checksum invalid\n"); + return false; + } + return true; +} + +bool +mxms_valid(struct nvkm_mxm *mxm) +{ + u8 *mxms = mxms_data(mxm); + if (*(u32 *)mxms != 0x5f4d584d) { + nvkm_debug(&mxm->subdev, "signature invalid\n"); + return false; + } + + if (!mxms_version(mxm) || !mxms_checksum(mxm)) + return false; + + return true; +} + +bool +mxms_foreach(struct nvkm_mxm *mxm, u8 types, + bool (*exec)(struct nvkm_mxm *, u8 *, void *), void *info) +{ + struct nvkm_subdev *subdev = &mxm->subdev; + u8 *mxms = mxms_data(mxm); + u8 *desc = mxms + mxms_headerlen(mxm); + u8 *fini = desc + mxms_structlen(mxm) - 1; + while (desc < fini) { + u8 type = desc[0] & 0x0f; + u8 headerlen = 0; + u8 recordlen = 0; + u8 entries = 0; + + switch (type) { + case 0: /* Output Device Structure */ + if (mxms_version(mxm) >= 0x0300) + headerlen = 8; + else + headerlen = 6; + break; + case 1: /* System Cooling Capability Structure */ + case 2: /* Thermal Structure */ + case 3: /* Input Power Structure */ + headerlen = 4; + break; + case 4: /* GPIO Device Structure */ + headerlen = 4; + recordlen = 2; + entries = (ROM32(desc[0]) & 0x01f00000) >> 20; + break; + case 5: /* Vendor Specific Structure */ + headerlen = 8; + break; + case 6: /* Backlight Control Structure */ + if (mxms_version(mxm) >= 0x0300) { + headerlen = 4; + recordlen = 8; + entries = (desc[1] & 0xf0) >> 4; + } else { + headerlen = 8; + } + break; + case 7: /* Fan Control Structure */ + headerlen = 8; + recordlen = 4; + entries = desc[1] & 0x07; + break; + default: + nvkm_debug(subdev, "unknown descriptor type %d\n", type); + return false; + } + + if (mxm->subdev.debug >= NV_DBG_DEBUG && (exec == NULL)) { + static const char * mxms_desc[] = { + "ODS", "SCCS", "TS", "IPS", + "GSD", "VSS", "BCS", "FCS", + }; + u8 *dump = desc; + char data[32], *ptr; + int i, j; + + for (j = headerlen - 1, ptr = data; j >= 0; j--) + ptr += sprintf(ptr, "%02x", dump[j]); + dump += headerlen; + + nvkm_debug(subdev, "%4s: %s\n", mxms_desc[type], data); + for (i = 0; i < entries; i++, dump += recordlen) { + for (j = recordlen - 1, ptr = data; j >= 0; j--) + ptr += sprintf(ptr, "%02x", dump[j]); + nvkm_debug(subdev, " %s\n", data); + } + } + + if (types & (1 << type)) { + if (!exec(mxm, desc, info)) + return false; + } + + desc += headerlen + (entries * recordlen); + } + + return true; +} + +void +mxms_output_device(struct nvkm_mxm *mxm, u8 *pdata, struct mxms_odev *desc) +{ + u64 data = ROM32(pdata[0]); + if (mxms_version(mxm) >= 0x0300) + data |= (u64)ROM16(pdata[4]) << 32; + + desc->outp_type = (data & 0x00000000000000f0ULL) >> 4; + desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8; + desc->conn_type = (data & 0x000000000001f000ULL) >> 12; + desc->dig_conn = (data & 0x0000000000780000ULL) >> 19; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h new file mode 100644 index 000000000..d9676b282 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVMXM_MXMS_H__ +#define __NVMXM_MXMS_H__ +#include "priv.h" + +struct mxms_odev { + u8 outp_type; + u8 conn_type; + u8 ddc_port; + u8 dig_conn; +}; + +void mxms_output_device(struct nvkm_mxm *, u8 *, struct mxms_odev *); + +u16 mxms_version(struct nvkm_mxm *); +u16 mxms_headerlen(struct nvkm_mxm *); +u16 mxms_structlen(struct nvkm_mxm *); +bool mxms_checksum(struct nvkm_mxm *); +bool mxms_valid(struct nvkm_mxm *); + +bool mxms_foreach(struct nvkm_mxm *, u8, + bool (*)(struct nvkm_mxm *, u8 *, void *), void *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c new file mode 100644 index 000000000..f3167904d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c @@ -0,0 +1,220 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "mxms.h" + +#include +#include +#include +#include + +struct context { + u32 *outp; + struct mxms_odev desc; +}; + +static bool +mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info) +{ + struct context *ctx = info; + struct mxms_odev desc; + + mxms_output_device(mxm, data, &desc); + if (desc.outp_type == 2 && + desc.dig_conn == ctx->desc.dig_conn) + return false; + return true; +} + +static bool +mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info) +{ + struct nvkm_bios *bios = mxm->subdev.device->bios; + struct context *ctx = info; + u64 desc = *(u64 *)data; + + mxms_output_device(mxm, data, &ctx->desc); + + /* match dcb encoder type to mxm-ods device type */ + if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type) + return true; + + /* digital output, have some extra stuff to match here, there's a + * table in the vbios that provides a mapping from the mxm digital + * connection enum values to SOR/link + */ + if ((desc & 0x00000000000000f0) >= 0x20) { + /* check against sor index */ + u8 link = mxm_sor_map(bios, ctx->desc.dig_conn); + if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24) + return true; + + /* check dcb entry has a compatible link field */ + link = (link & 0x30) >> 4; + if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link) + return true; + } + + /* mark this descriptor accounted for by setting invalid device type, + * except of course some manufactures don't follow specs properly and + * we need to avoid killing off the TMDS function on DP connectors + * if MXM-SIS is missing an entry for it. + */ + data[0] &= ~0xf0; + if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 && + mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) { + data[0] |= 0x20; /* modify descriptor to match TMDS now */ + } else { + data[0] |= 0xf0; + } + + return false; +} + +static int +mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb) +{ + struct nvkm_mxm *mxm = data; + struct context ctx = { .outp = (u32 *)(bios->data + pdcb) }; + u8 type, i2cidx, link, ver, len; + u8 *conn; + + /* look for an output device structure that matches this dcb entry. + * if one isn't found, disable it. + */ + if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) { + nvkm_debug(&mxm->subdev, "disable %d: %08x %08x\n", + idx, ctx.outp[0], ctx.outp[1]); + ctx.outp[0] |= 0x0000000f; + return 0; + } + + /* modify the output's ddc/aux port, there's a pointer to a table + * with the mapping from mxm ddc/aux port to dcb i2c_index in the + * vbios mxm table + */ + i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port); + if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP) + i2cidx = (i2cidx & 0x0f) << 4; + else + i2cidx = (i2cidx & 0xf0); + + if (i2cidx != 0xf0) { + ctx.outp[0] &= ~0x000000f0; + ctx.outp[0] |= i2cidx; + } + + /* override dcb sorconf.link, based on what mxm data says */ + switch (ctx.desc.outp_type) { + case 0x00: /* Analog CRT */ + case 0x01: /* Analog TV/HDTV */ + break; + default: + link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30; + ctx.outp[1] &= ~0x00000030; + ctx.outp[1] |= link; + break; + } + + /* we may need to fixup various other vbios tables based on what + * the descriptor says the connector type should be. + * + * in a lot of cases, the vbios tables will claim DVI-I is possible, + * and the mxm data says the connector is really HDMI. another + * common example is DP->eDP. + */ + conn = bios->data; + conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len); + type = conn[0]; + switch (ctx.desc.conn_type) { + case 0x01: /* LVDS */ + ctx.outp[1] |= 0x00000004; /* use_power_scripts */ + /* XXX: modify default link width in LVDS table */ + break; + case 0x02: /* HDMI */ + type = DCB_CONNECTOR_HDMI_1; + break; + case 0x03: /* DVI-D */ + type = DCB_CONNECTOR_DVI_D; + break; + case 0x0e: /* eDP, falls through to DPint */ + ctx.outp[1] |= 0x00010000; + fallthrough; + case 0x07: /* DP internal, wtf is this?? HP8670w */ + ctx.outp[1] |= 0x00000004; /* use_power_scripts? */ + type = DCB_CONNECTOR_eDP; + break; + default: + break; + } + + if (mxms_version(mxm) >= 0x0300) + conn[0] = type; + + return 0; +} + +static bool +mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info) +{ + struct nvkm_subdev *subdev = &mxm->subdev; + u64 desc = *(u64 *)data; + if ((desc & 0xf0) != 0xf0) + nvkm_info(subdev, "unmatched output device %016llx\n", desc); + return true; +} + +static void +mxm_dcb_sanitise(struct nvkm_mxm *mxm) +{ + struct nvkm_subdev *subdev = &mxm->subdev; + struct nvkm_bios *bios = subdev->device->bios; + u8 ver, hdr, cnt, len; + u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len); + if (dcb == 0x0000 || (ver != 0x40 && ver != 0x41)) { + nvkm_warn(subdev, "unsupported DCB version\n"); + return; + } + + dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry); + mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL); +} + +int +nv50_mxm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_subdev **pmxm) +{ + struct nvkm_mxm *mxm; + int ret; + + ret = nvkm_mxm_new_(device, type, inst, &mxm); + if (mxm) + *pmxm = &mxm->subdev; + if (ret) + return ret; + + if (mxm->action & MXM_SANITISE_DCB) + mxm_dcb_sanitise(mxm); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/priv.h new file mode 100644 index 000000000..fcacb6c6a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/priv.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_MXM_PRIV_H__ +#define __NVKM_MXM_PRIV_H__ +#define nvkm_mxm(p) container_of((p), struct nvkm_mxm, subdev) +#include + +#define MXM_SANITISE_DCB 0x00000001 + +struct nvkm_mxm { + struct nvkm_subdev subdev; + u32 action; + u8 *mxms; +}; + +int nvkm_mxm_new_(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_mxm **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild new file mode 100644 index 000000000..174bdf995 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/pci/agp.o +nvkm-y += nvkm/subdev/pci/base.o +nvkm-y += nvkm/subdev/pci/pcie.o +nvkm-y += nvkm/subdev/pci/nv04.o +nvkm-y += nvkm/subdev/pci/nv40.o +nvkm-y += nvkm/subdev/pci/nv46.o +nvkm-y += nvkm/subdev/pci/nv4c.o +nvkm-y += nvkm/subdev/pci/g84.o +nvkm-y += nvkm/subdev/pci/g92.o +nvkm-y += nvkm/subdev/pci/g94.o +nvkm-y += nvkm/subdev/pci/gf100.o +nvkm-y += nvkm/subdev/pci/gf106.o +nvkm-y += nvkm/subdev/pci/gk104.o +nvkm-y += nvkm/subdev/pci/gp100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c new file mode 100644 index 000000000..385a90f91 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c @@ -0,0 +1,175 @@ +/* + * Copyright 2015 Nouveau Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "agp.h" +#ifdef __NVKM_PCI_AGP_H__ +#include + +struct nvkm_device_agp_quirk { + u16 hostbridge_vendor; + u16 hostbridge_device; + u16 chip_vendor; + u16 chip_device; + int mode; +}; + +static const struct nvkm_device_agp_quirk +nvkm_device_agp_quirks[] = { + /* VIA Apollo PRO133x / GeForce FX 5600 Ultra - fdo#20341 */ + { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_NVIDIA, 0x0311, 2 }, + /* SiS 761 does not support AGP cards, use PCI mode */ + { PCI_VENDOR_ID_SI, 0x0761, PCI_ANY_ID, PCI_ANY_ID, 0 }, + {}, +}; + +void +nvkm_agp_fini(struct nvkm_pci *pci) +{ + if (pci->agp.acquired) { + agp_backend_release(pci->agp.bridge); + pci->agp.acquired = false; + } +} + +/* Ensure AGP controller is in a consistent state in case we need to + * execute the VBIOS DEVINIT scripts. + */ +void +nvkm_agp_preinit(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + u32 mode = nvkm_pci_rd32(pci, 0x004c); + u32 save[2]; + + /* First of all, disable fast writes, otherwise if it's already + * enabled in the AGP bridge and we disable the card's AGP + * controller we might be locking ourselves out of it. + */ + if ((mode | pci->agp.mode) & PCI_AGP_COMMAND_FW) { + mode = pci->agp.mode & ~PCI_AGP_COMMAND_FW; + agp_enable(pci->agp.bridge, mode); + } + + /* clear busmaster bit, and disable AGP */ + save[0] = nvkm_pci_rd32(pci, 0x0004); + nvkm_pci_wr32(pci, 0x0004, save[0] & ~0x00000004); + nvkm_pci_wr32(pci, 0x004c, 0x00000000); + + /* reset PGRAPH, PFIFO and PTIMER */ + save[1] = nvkm_mask(device, 0x000200, 0x00011100, 0x00000000); + nvkm_mask(device, 0x000200, 0x00011100, save[1]); + + /* and restore busmaster bit (gives effect of resetting AGP) */ + nvkm_pci_wr32(pci, 0x0004, save[0]); +} + +int +nvkm_agp_init(struct nvkm_pci *pci) +{ + if (!agp_backend_acquire(pci->pdev)) { + nvkm_error(&pci->subdev, "failed to acquire agp\n"); + return -ENODEV; + } + + agp_enable(pci->agp.bridge, pci->agp.mode); + pci->agp.acquired = true; + return 0; +} + +void +nvkm_agp_dtor(struct nvkm_pci *pci) +{ + arch_phys_wc_del(pci->agp.mtrr); +} + +void +nvkm_agp_ctor(struct nvkm_pci *pci) +{ + const struct nvkm_device_agp_quirk *quirk = nvkm_device_agp_quirks; + struct nvkm_subdev *subdev = &pci->subdev; + struct nvkm_device *device = subdev->device; + struct agp_kern_info info; + int mode = -1; + +#ifdef __powerpc__ + /* Disable AGP by default on all PowerPC machines for now -- At + * least some UniNorth-2 AGP bridges are known to be broken: + * DMA from the host to the card works just fine, but writeback + * from the card to the host goes straight to memory + * untranslated bypassing that GATT somehow, making them quite + * painful to deal with... + */ + mode = 0; +#endif + mode = nvkm_longopt(device->cfgopt, "NvAGP", mode); + + /* acquire bridge temporarily, so that we can copy its info */ + if (!(pci->agp.bridge = agp_backend_acquire(pci->pdev))) { + nvkm_warn(subdev, "failed to acquire agp\n"); + return; + } + agp_copy_info(pci->agp.bridge, &info); + agp_backend_release(pci->agp.bridge); + + pci->agp.mode = info.mode; + pci->agp.base = info.aper_base; + pci->agp.size = info.aper_size * 1024 * 1024; + pci->agp.cma = info.cant_use_aperture; + pci->agp.mtrr = -1; + + /* determine if bridge + chipset combination needs a workaround */ + while (quirk->hostbridge_vendor) { + if (info.device->vendor == quirk->hostbridge_vendor && + info.device->device == quirk->hostbridge_device && + (quirk->chip_vendor == (u16)PCI_ANY_ID || + pci->pdev->vendor == quirk->chip_vendor) && + (quirk->chip_device == (u16)PCI_ANY_ID || + pci->pdev->device == quirk->chip_device)) { + nvkm_info(subdev, "forcing default agp mode to %dX, " + "use NvAGP= to override\n", + quirk->mode); + mode = quirk->mode; + break; + } + quirk++; + } + + /* apply quirk / user-specified mode */ + if (mode >= 1) { + if (pci->agp.mode & 0x00000008) + mode /= 4; /* AGPv3 */ + pci->agp.mode &= ~0x00000007; + pci->agp.mode |= (mode & 0x7); + } else + if (mode == 0) { + pci->agp.bridge = NULL; + return; + } + + /* fast writes appear to be broken on nv18, they make the card + * lock up randomly. + */ + if (device->chipset == 0x18) + pci->agp.mode &= ~PCI_AGP_COMMAND_FW; + + pci->agp.mtrr = arch_phys_wc_add(pci->agp.base, pci->agp.size); +} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h new file mode 100644 index 000000000..ad4d3621d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +#include "priv.h" +#if defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE)) +#ifndef __NVKM_PCI_AGP_H__ +#define __NVKM_PCI_AGP_H__ + +void nvkm_agp_ctor(struct nvkm_pci *); +void nvkm_agp_dtor(struct nvkm_pci *); +void nvkm_agp_preinit(struct nvkm_pci *); +int nvkm_agp_init(struct nvkm_pci *); +void nvkm_agp_fini(struct nvkm_pci *); +#endif +#else +static inline void nvkm_agp_ctor(struct nvkm_pci *pci) {} +static inline void nvkm_agp_dtor(struct nvkm_pci *pci) {} +static inline void nvkm_agp_preinit(struct nvkm_pci *pci) {} +static inline int nvkm_agp_init(struct nvkm_pci *pci) { return -ENOSYS; } +static inline void nvkm_agp_fini(struct nvkm_pci *pci) {} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c new file mode 100644 index 000000000..a7d42ea8b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c @@ -0,0 +1,232 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "agp.h" + +#include +#include +#include + +u32 +nvkm_pci_rd32(struct nvkm_pci *pci, u16 addr) +{ + return pci->func->rd32(pci, addr); +} + +void +nvkm_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) +{ + pci->func->wr08(pci, addr, data); +} + +void +nvkm_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) +{ + pci->func->wr32(pci, addr, data); +} + +u32 +nvkm_pci_mask(struct nvkm_pci *pci, u16 addr, u32 mask, u32 value) +{ + u32 data = pci->func->rd32(pci, addr); + pci->func->wr32(pci, addr, (data & ~mask) | value); + return data; +} + +void +nvkm_pci_rom_shadow(struct nvkm_pci *pci, bool shadow) +{ + u32 data = nvkm_pci_rd32(pci, 0x0050); + if (shadow) + data |= 0x00000001; + else + data &= ~0x00000001; + nvkm_pci_wr32(pci, 0x0050, data); +} + +static irqreturn_t +nvkm_pci_intr(int irq, void *arg) +{ + struct nvkm_pci *pci = arg; + struct nvkm_device *device = pci->subdev.device; + bool handled = false; + + if (pci->irq < 0) + return IRQ_HANDLED; + + nvkm_mc_intr_unarm(device); + if (pci->msi) + pci->func->msi_rearm(pci); + nvkm_mc_intr(device, &handled); + nvkm_mc_intr_rearm(device); + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static int +nvkm_pci_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + + if (pci->agp.bridge) + nvkm_agp_fini(pci); + + return 0; +} + +static int +nvkm_pci_preinit(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + if (pci->agp.bridge) + nvkm_agp_preinit(pci); + return 0; +} + +static int +nvkm_pci_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + struct pci_dev *pdev = pci->pdev; + int ret; + + if (pci_is_pcie(pci->pdev)) { + ret = nvkm_pcie_oneinit(pci); + if (ret) + return ret; + } + + ret = request_irq(pdev->irq, nvkm_pci_intr, IRQF_SHARED, "nvkm", pci); + if (ret) + return ret; + + pci->irq = pdev->irq; + return 0; +} + +static int +nvkm_pci_init(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + int ret; + + if (pci->agp.bridge) { + ret = nvkm_agp_init(pci); + if (ret) + return ret; + } else if (pci_is_pcie(pci->pdev)) { + nvkm_pcie_init(pci); + } + + if (pci->func->init) + pci->func->init(pci); + + /* Ensure MSI interrupts are armed, for the case where there are + * already interrupts pending (for whatever reason) at load time. + */ + if (pci->msi) + pci->func->msi_rearm(pci); + + return 0; +} + +static void * +nvkm_pci_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + + nvkm_agp_dtor(pci); + + if (pci->irq >= 0) { + /* freq_irq() will call the handler, we use pci->irq == -1 + * to signal that it's been torn down and should be a noop. + */ + int irq = pci->irq; + pci->irq = -1; + free_irq(irq, pci); + } + + if (pci->msi) + pci_disable_msi(pci->pdev); + + return nvkm_pci(subdev); +} + +static const struct nvkm_subdev_func +nvkm_pci_func = { + .dtor = nvkm_pci_dtor, + .oneinit = nvkm_pci_oneinit, + .preinit = nvkm_pci_preinit, + .init = nvkm_pci_init, + .fini = nvkm_pci_fini, +}; + +int +nvkm_pci_new_(const struct nvkm_pci_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_pci **ppci) +{ + struct nvkm_pci *pci; + + if (!(pci = *ppci = kzalloc(sizeof(**ppci), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_pci_func, device, type, inst, &pci->subdev); + pci->func = func; + pci->pdev = device->func->pci(device)->pdev; + pci->irq = -1; + pci->pcie.speed = -1; + pci->pcie.width = -1; + + if (device->type == NVKM_DEVICE_AGP) + nvkm_agp_ctor(pci); + + switch (pci->pdev->device & 0x0ff0) { + case 0x00f0: + case 0x02e0: + /* BR02? NFI how these would be handled yet exactly */ + break; + default: + switch (device->chipset) { + case 0xaa: + /* reported broken, nv also disable it */ + break; + default: + pci->msi = true; + break; + } + } + +#ifdef __BIG_ENDIAN + pci->msi = false; +#endif + + pci->msi = nvkm_boolopt(device->cfgopt, "NvMSI", pci->msi); + if (pci->msi && func->msi_rearm) { + pci->msi = pci_enable_msi(pci->pdev) == 0; + if (pci->msi) + nvkm_debug(&pci->subdev, "MSI enabled\n"); + } else { + pci->msi = false; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c new file mode 100644 index 000000000..5b29aaced --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c @@ -0,0 +1,157 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +static int +g84_pcie_version_supported(struct nvkm_pci *pci) +{ + /* g84 and g86 report wrong information about what they support */ + return 1; +} + +int +g84_pcie_version(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + return (nvkm_rd32(device, 0x00154c) & 0x1) + 1; +} + +void +g84_pcie_set_version(struct nvkm_pci *pci, u8 ver) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x00154c, 0x1, (ver >= 2 ? 0x1 : 0x0)); +} + +static void +g84_pcie_set_cap_speed(struct nvkm_pci *pci, bool full_speed) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x00154c, 0x80, full_speed ? 0x80 : 0x0); +} + +enum nvkm_pcie_speed +g84_pcie_cur_speed(struct nvkm_pci *pci) +{ + u32 reg_v = nvkm_pci_rd32(pci, 0x88) & 0x30000; + switch (reg_v) { + case 0x30000: + return NVKM_PCIE_SPEED_8_0; + case 0x20000: + return NVKM_PCIE_SPEED_5_0; + case 0x10000: + default: + return NVKM_PCIE_SPEED_2_5; + } +} + +enum nvkm_pcie_speed +g84_pcie_max_speed(struct nvkm_pci *pci) +{ + u32 reg_v = nvkm_pci_rd32(pci, 0x460) & 0x3300; + if (reg_v == 0x2200) + return NVKM_PCIE_SPEED_5_0; + return NVKM_PCIE_SPEED_2_5; +} + +void +g84_pcie_set_link_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + u32 mask_value; + + if (speed == NVKM_PCIE_SPEED_5_0) + mask_value = 0x20; + else + mask_value = 0x10; + + nvkm_pci_mask(pci, 0x460, 0x30, mask_value); + nvkm_pci_mask(pci, 0x460, 0x1, 0x1); +} + +int +g84_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + g84_pcie_set_cap_speed(pci, speed == NVKM_PCIE_SPEED_5_0); + g84_pcie_set_link_speed(pci, speed); + return 0; +} + +void +g84_pci_init(struct nvkm_pci *pci) +{ + /* The following only concerns PCIe cards. */ + if (!pci_is_pcie(pci->pdev)) + return; + + /* Tag field is 8-bit long, regardless of EXT_TAG. + * However, if EXT_TAG is disabled, only the lower 5 bits of the tag + * field should be used, limiting the number of request to 32. + * + * Apparently, 0x041c stores some limit on the number of requests + * possible, so if EXT_TAG is disabled, limit that requests number to + * 32 + * + * Fixes fdo#86537 + */ + if (nvkm_pci_rd32(pci, 0x007c) & 0x00000020) + nvkm_pci_mask(pci, 0x0080, 0x00000100, 0x00000100); + else + nvkm_pci_mask(pci, 0x041c, 0x00000060, 0x00000000); +} + +int +g84_pcie_init(struct nvkm_pci *pci) +{ + bool full_speed = g84_pcie_cur_speed(pci) == NVKM_PCIE_SPEED_5_0; + g84_pcie_set_cap_speed(pci, full_speed); + return 0; +} + +static const struct nvkm_pci_func +g84_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv46_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g84_pcie_version_supported, +}; + +int +g84_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&g84_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c new file mode 100644 index 000000000..a9e067400 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +int +g92_pcie_version_supported(struct nvkm_pci *pci) +{ + if ((nvkm_pci_rd32(pci, 0x460) & 0x200) == 0x200) + return 2; + return 1; +} + +static const struct nvkm_pci_func +g92_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv46_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +g92_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&g92_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c new file mode 100644 index 000000000..7bacd0693 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_pci_func +g94_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +g94_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&g94_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c new file mode 100644 index 000000000..099906092 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c @@ -0,0 +1,103 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static void +gf100_pci_msi_rearm(struct nvkm_pci *pci) +{ + nvkm_pci_wr08(pci, 0x0704, 0xff); +} + +void +gf100_pcie_set_version(struct nvkm_pci *pci, u8 ver) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x02241c, 0x1, ver > 1 ? 1 : 0); +} + +int +gf100_pcie_version(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + return (nvkm_rd32(device, 0x02241c) & 0x1) + 1; +} + +void +gf100_pcie_set_cap_speed(struct nvkm_pci *pci, bool full_speed) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x02241c, 0x80, full_speed ? 0x80 : 0x0); +} + +int +gf100_pcie_cap_speed(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + u8 punits_pci_cap_speed = nvkm_rd32(device, 0x02241c) & 0x80; + if (punits_pci_cap_speed == 0x80) + return 1; + return 0; +} + +int +gf100_pcie_init(struct nvkm_pci *pci) +{ + bool full_speed = g84_pcie_cur_speed(pci) == NVKM_PCIE_SPEED_5_0; + gf100_pcie_set_cap_speed(pci, full_speed); + return 0; +} + +int +gf100_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + gf100_pcie_set_cap_speed(pci, speed == NVKM_PCIE_SPEED_5_0); + g84_pcie_set_link_speed(pci, speed); + return 0; +} + +static const struct nvkm_pci_func +gf100_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = gf100_pci_msi_rearm, + + .pcie.init = gf100_pcie_init, + .pcie.set_link = gf100_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = gf100_pcie_set_version, + .pcie.version = gf100_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +gf100_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gf100_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c new file mode 100644 index 000000000..bcde609ba --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Karol Herbst + * + * 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: Karol Herbst + */ +#include "priv.h" + +static const struct nvkm_pci_func +gf106_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, + + .pcie.init = gf100_pcie_init, + .pcie.set_link = gf100_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = gf100_pcie_set_version, + .pcie.version = gf100_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +gf106_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gf106_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c new file mode 100644 index 000000000..6be87ecff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c @@ -0,0 +1,229 @@ +/* + * Copyright 2015 Karol Herbst + * + * 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: Karol Herbst + */ +#include "priv.h" + +static int +gk104_pcie_version_supported(struct nvkm_pci *pci) +{ + return (nvkm_rd32(pci->subdev.device, 0x8c1c0) & 0x4) == 0x4 ? 2 : 1; +} + +static void +gk104_pcie_set_cap_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + struct nvkm_device *device = pci->subdev.device; + + switch (speed) { + case NVKM_PCIE_SPEED_2_5: + gf100_pcie_set_cap_speed(pci, false); + nvkm_mask(device, 0x8c1c0, 0x30000, 0x10000); + break; + case NVKM_PCIE_SPEED_5_0: + gf100_pcie_set_cap_speed(pci, true); + nvkm_mask(device, 0x8c1c0, 0x30000, 0x20000); + break; + case NVKM_PCIE_SPEED_8_0: + gf100_pcie_set_cap_speed(pci, true); + nvkm_mask(device, 0x8c1c0, 0x30000, 0x30000); + break; + } +} + +static enum nvkm_pcie_speed +gk104_pcie_cap_speed(struct nvkm_pci *pci) +{ + int speed = gf100_pcie_cap_speed(pci); + + if (speed == 0) + return NVKM_PCIE_SPEED_2_5; + + if (speed >= 1) { + int speed2 = nvkm_rd32(pci->subdev.device, 0x8c1c0) & 0x30000; + switch (speed2) { + case 0x00000: + case 0x10000: + return NVKM_PCIE_SPEED_2_5; + case 0x20000: + return NVKM_PCIE_SPEED_5_0; + case 0x30000: + return NVKM_PCIE_SPEED_8_0; + } + } + + return -EINVAL; +} + +static void +gk104_pcie_set_lnkctl_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + u8 reg_v = 0; + switch (speed) { + case NVKM_PCIE_SPEED_2_5: + reg_v = 1; + break; + case NVKM_PCIE_SPEED_5_0: + reg_v = 2; + break; + case NVKM_PCIE_SPEED_8_0: + reg_v = 3; + break; + } + nvkm_pci_mask(pci, 0xa8, 0x3, reg_v); +} + +static enum nvkm_pcie_speed +gk104_pcie_lnkctl_speed(struct nvkm_pci *pci) +{ + u8 reg_v = nvkm_pci_rd32(pci, 0xa8) & 0x3; + switch (reg_v) { + case 0: + case 1: + return NVKM_PCIE_SPEED_2_5; + case 2: + return NVKM_PCIE_SPEED_5_0; + case 3: + return NVKM_PCIE_SPEED_8_0; + } + return -1; +} + +static enum nvkm_pcie_speed +gk104_pcie_max_speed(struct nvkm_pci *pci) +{ + u32 max_speed = nvkm_rd32(pci->subdev.device, 0x8c1c0) & 0x300000; + switch (max_speed) { + case 0x000000: + return NVKM_PCIE_SPEED_8_0; + case 0x100000: + return NVKM_PCIE_SPEED_5_0; + case 0x200000: + return NVKM_PCIE_SPEED_2_5; + } + return NVKM_PCIE_SPEED_2_5; +} + +static void +gk104_pcie_set_link_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + struct nvkm_device *device = pci->subdev.device; + u32 mask_value; + + switch (speed) { + case NVKM_PCIE_SPEED_8_0: + mask_value = 0x00000; + break; + case NVKM_PCIE_SPEED_5_0: + mask_value = 0x40000; + break; + case NVKM_PCIE_SPEED_2_5: + default: + mask_value = 0x80000; + break; + } + + nvkm_mask(device, 0x8c040, 0xc0000, mask_value); + nvkm_mask(device, 0x8c040, 0x1, 0x1); +} + +static int +gk104_pcie_init(struct nvkm_pci * pci) +{ + enum nvkm_pcie_speed lnkctl_speed, max_speed, cap_speed; + struct nvkm_subdev *subdev = &pci->subdev; + + if (gf100_pcie_version(pci) < 2) + return 0; + + lnkctl_speed = gk104_pcie_lnkctl_speed(pci); + max_speed = gk104_pcie_max_speed(pci); + cap_speed = gk104_pcie_cap_speed(pci); + + if (cap_speed != max_speed) { + nvkm_trace(subdev, "adjusting cap to max speed\n"); + gk104_pcie_set_cap_speed(pci, max_speed); + cap_speed = gk104_pcie_cap_speed(pci); + if (cap_speed != max_speed) + nvkm_warn(subdev, "failed to adjust cap speed\n"); + } + + if (lnkctl_speed != max_speed) { + nvkm_debug(subdev, "adjusting lnkctl to max speed\n"); + gk104_pcie_set_lnkctl_speed(pci, max_speed); + lnkctl_speed = gk104_pcie_lnkctl_speed(pci); + if (lnkctl_speed != max_speed) + nvkm_error(subdev, "failed to adjust lnkctl speed\n"); + } + + return 0; +} + +static int +gk104_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + struct nvkm_subdev *subdev = &pci->subdev; + enum nvkm_pcie_speed lnk_ctl_speed = gk104_pcie_lnkctl_speed(pci); + enum nvkm_pcie_speed lnk_cap_speed = gk104_pcie_cap_speed(pci); + + if (speed > lnk_cap_speed) { + speed = lnk_cap_speed; + nvkm_warn(subdev, "dropping requested speed due too low cap" + " speed\n"); + } + + if (speed > lnk_ctl_speed) { + speed = lnk_ctl_speed; + nvkm_warn(subdev, "dropping requested speed due too low" + " lnkctl speed\n"); + } + + gk104_pcie_set_link_speed(pci, speed); + return 0; +} + + +static const struct nvkm_pci_func +gk104_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, + + .pcie.init = gk104_pcie_init, + .pcie.set_link = gk104_pcie_set_link, + + .pcie.max_speed = gk104_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = gf100_pcie_set_version, + .pcie.version = gf100_pcie_version, + .pcie.version_supported = gk104_pcie_version_supported, +}; + +int +gk104_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gk104_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c new file mode 100644 index 000000000..a5fafda00 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static void +gp100_pci_msi_rearm(struct nvkm_pci *pci) +{ + nvkm_pci_wr32(pci, 0x0704, 0x00000000); +} + +static const struct nvkm_pci_func +gp100_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = gp100_pci_msi_rearm, +}; + +int +gp100_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gp100_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c new file mode 100644 index 000000000..9ab64194b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c @@ -0,0 +1,59 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static u32 +nv04_pci_rd32(struct nvkm_pci *pci, u16 addr) +{ + struct nvkm_device *device = pci->subdev.device; + return nvkm_rd32(device, 0x001800 + addr); +} + +static void +nv04_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr08(device, 0x001800 + addr, data); +} + +static void +nv04_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr32(device, 0x001800 + addr, data); +} + +static const struct nvkm_pci_func +nv04_pci_func = { + .rd32 = nv04_pci_rd32, + .wr08 = nv04_pci_wr08, + .wr32 = nv04_pci_wr32, +}; + +int +nv04_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv04_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c new file mode 100644 index 000000000..6a3c31cf0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c @@ -0,0 +1,66 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +u32 +nv40_pci_rd32(struct nvkm_pci *pci, u16 addr) +{ + struct nvkm_device *device = pci->subdev.device; + return nvkm_rd32(device, 0x088000 + addr); +} + +void +nv40_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr08(device, 0x088000 + addr, data); +} + +void +nv40_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr32(device, 0x088000 + addr, data); +} + +void +nv40_pci_msi_rearm(struct nvkm_pci *pci) +{ + nvkm_pci_wr08(pci, 0x0068, 0xff); +} + +static const struct nvkm_pci_func +nv40_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, +}; + +int +nv40_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv40_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c new file mode 100644 index 000000000..9cad17f17 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c @@ -0,0 +1,52 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +/* MSI re-arm through the PRI appears to be broken on NV46/NV50/G84/G86/G92, + * so we access it via alternate PCI config space mechanisms. + */ +void +nv46_pci_msi_rearm(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + struct pci_dev *pdev = device->func->pci(device)->pdev; + pci_write_config_byte(pdev, 0x68, 0xff); +} + +static const struct nvkm_pci_func +nv46_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv46_pci_msi_rearm, +}; + +int +nv46_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv46_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c new file mode 100644 index 000000000..741e34bf3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c @@ -0,0 +1,38 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_pci_func +nv4c_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, +}; + +int +nv4c_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv4c_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c new file mode 100644 index 000000000..d71e5db50 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c @@ -0,0 +1,165 @@ +/* + * Copyright 2015 Karol Herbst + * + * 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: Karol Herbst + */ +#include "priv.h" + +static char *nvkm_pcie_speeds[] = { + "2.5GT/s", + "5.0GT/s", + "8.0GT/s", +}; + +static enum nvkm_pcie_speed +nvkm_pcie_speed(enum pci_bus_speed speed) +{ + switch (speed) { + case PCIE_SPEED_2_5GT: + return NVKM_PCIE_SPEED_2_5; + case PCIE_SPEED_5_0GT: + return NVKM_PCIE_SPEED_5_0; + case PCIE_SPEED_8_0GT: + return NVKM_PCIE_SPEED_8_0; + default: + /* XXX 0x16 is 8_0, assume 0x17 will be 16_0 for now */ + if (speed == 0x17) + return NVKM_PCIE_SPEED_8_0; + return -1; + } +} + +static int +nvkm_pcie_get_version(struct nvkm_pci *pci) +{ + if (!pci->func->pcie.version) + return -ENOSYS; + + return pci->func->pcie.version(pci); +} + +static int +nvkm_pcie_get_max_version(struct nvkm_pci *pci) +{ + if (!pci->func->pcie.version_supported) + return -ENOSYS; + + return pci->func->pcie.version_supported(pci); +} + +static int +nvkm_pcie_set_version(struct nvkm_pci *pci, int version) +{ + if (!pci->func->pcie.set_version) + return -ENOSYS; + + nvkm_trace(&pci->subdev, "set to version %i\n", version); + pci->func->pcie.set_version(pci, version); + return nvkm_pcie_get_version(pci); +} + +int +nvkm_pcie_oneinit(struct nvkm_pci *pci) +{ + if (pci->func->pcie.max_speed) + nvkm_debug(&pci->subdev, "pcie max speed: %s\n", + nvkm_pcie_speeds[pci->func->pcie.max_speed(pci)]); + return 0; +} + +int +nvkm_pcie_init(struct nvkm_pci *pci) +{ + struct nvkm_subdev *subdev = &pci->subdev; + int ret; + + /* raise pcie version first */ + ret = nvkm_pcie_get_version(pci); + if (ret > 0) { + int max_version = nvkm_pcie_get_max_version(pci); + if (max_version > 0 && max_version > ret) + ret = nvkm_pcie_set_version(pci, max_version); + + if (ret < max_version) + nvkm_error(subdev, "couldn't raise version: %i\n", ret); + } + + if (pci->func->pcie.init) + pci->func->pcie.init(pci); + + if (pci->pcie.speed != -1) + nvkm_pcie_set_link(pci, pci->pcie.speed, pci->pcie.width); + + return 0; +} + +int +nvkm_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + struct nvkm_subdev *subdev = &pci->subdev; + enum nvkm_pcie_speed cur_speed, max_speed; + struct pci_bus *pbus; + int ret; + + if (!pci || !pci_is_pcie(pci->pdev)) + return 0; + pbus = pci->pdev->bus; + + if (!pci->func->pcie.set_link) + return -ENOSYS; + + nvkm_trace(subdev, "requested %s\n", nvkm_pcie_speeds[speed]); + + if (pci->func->pcie.version(pci) < 2) { + nvkm_error(subdev, "setting link failed due to low version\n"); + return -ENODEV; + } + + cur_speed = pci->func->pcie.cur_speed(pci); + max_speed = min(nvkm_pcie_speed(pbus->max_bus_speed), + pci->func->pcie.max_speed(pci)); + + nvkm_trace(subdev, "current speed: %s\n", nvkm_pcie_speeds[cur_speed]); + + if (speed > max_speed) { + nvkm_debug(subdev, "%s not supported by bus or card, dropping" + "requested speed to %s", nvkm_pcie_speeds[speed], + nvkm_pcie_speeds[max_speed]); + speed = max_speed; + } + + pci->pcie.speed = speed; + pci->pcie.width = width; + + if (speed == cur_speed) { + nvkm_debug(subdev, "requested matches current speed\n"); + return speed; + } + + nvkm_debug(subdev, "set link to %s x%i\n", + nvkm_pcie_speeds[speed], width); + + ret = pci->func->pcie.set_link(pci, speed, width); + if (ret < 0) + nvkm_error(subdev, "setting link failed: %i\n", ret); + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h new file mode 100644 index 000000000..9b7583532 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PCI_PRIV_H__ +#define __NVKM_PCI_PRIV_H__ +#define nvkm_pci(p) container_of((p), struct nvkm_pci, subdev) +#include + +int nvkm_pci_new_(const struct nvkm_pci_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_pci **); + +struct nvkm_pci_func { + void (*init)(struct nvkm_pci *); + u32 (*rd32)(struct nvkm_pci *, u16 addr); + void (*wr08)(struct nvkm_pci *, u16 addr, u8 data); + void (*wr32)(struct nvkm_pci *, u16 addr, u32 data); + void (*msi_rearm)(struct nvkm_pci *); + + struct { + int (*init)(struct nvkm_pci *); + int (*set_link)(struct nvkm_pci *, enum nvkm_pcie_speed, u8); + + enum nvkm_pcie_speed (*max_speed)(struct nvkm_pci *); + enum nvkm_pcie_speed (*cur_speed)(struct nvkm_pci *); + + void (*set_version)(struct nvkm_pci *, u8); + int (*version)(struct nvkm_pci *); + int (*version_supported)(struct nvkm_pci *); + } pcie; +}; + +u32 nv40_pci_rd32(struct nvkm_pci *, u16); +void nv40_pci_wr08(struct nvkm_pci *, u16, u8); +void nv40_pci_wr32(struct nvkm_pci *, u16, u32); +void nv40_pci_msi_rearm(struct nvkm_pci *); + +void nv46_pci_msi_rearm(struct nvkm_pci *); + +void g84_pci_init(struct nvkm_pci *pci); + +/* pcie functions */ +void g84_pcie_set_version(struct nvkm_pci *, u8); +int g84_pcie_version(struct nvkm_pci *); +void g84_pcie_set_link_speed(struct nvkm_pci *, enum nvkm_pcie_speed); +enum nvkm_pcie_speed g84_pcie_cur_speed(struct nvkm_pci *); +enum nvkm_pcie_speed g84_pcie_max_speed(struct nvkm_pci *); +int g84_pcie_init(struct nvkm_pci *); +int g84_pcie_set_link(struct nvkm_pci *, enum nvkm_pcie_speed, u8); + +int g92_pcie_version_supported(struct nvkm_pci *); + +void gf100_pcie_set_version(struct nvkm_pci *, u8); +int gf100_pcie_version(struct nvkm_pci *); +void gf100_pcie_set_cap_speed(struct nvkm_pci *, bool); +int gf100_pcie_cap_speed(struct nvkm_pci *); +int gf100_pcie_init(struct nvkm_pci *); +int gf100_pcie_set_link(struct nvkm_pci *, enum nvkm_pcie_speed, u8); + +int nvkm_pcie_oneinit(struct nvkm_pci *); +int nvkm_pcie_init(struct nvkm_pci *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild new file mode 100644 index 000000000..eafc9321a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/pmu/base.o +nvkm-y += nvkm/subdev/pmu/memx.o +nvkm-y += nvkm/subdev/pmu/gt215.o +nvkm-y += nvkm/subdev/pmu/gf100.o +nvkm-y += nvkm/subdev/pmu/gf119.o +nvkm-y += nvkm/subdev/pmu/gk104.o +nvkm-y += nvkm/subdev/pmu/gk110.o +nvkm-y += nvkm/subdev/pmu/gk208.o +nvkm-y += nvkm/subdev/pmu/gk20a.o +nvkm-y += nvkm/subdev/pmu/gm107.o +nvkm-y += nvkm/subdev/pmu/gm200.o +nvkm-y += nvkm/subdev/pmu/gm20b.o +nvkm-y += nvkm/subdev/pmu/gp102.o +nvkm-y += nvkm/subdev/pmu/gp10b.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c new file mode 100644 index 000000000..455e95a89 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c @@ -0,0 +1,211 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include + +bool +nvkm_pmu_fan_controlled(struct nvkm_device *device) +{ + struct nvkm_pmu *pmu = device->pmu; + + /* Internal PMU FW does not currently control fans in any way, + * allow SW control of fans instead. + */ + if (pmu && pmu->func->code.size) + return false; + + /* Default (board-loaded, or VBIOS PMU/PREOS) PMU FW on Fermi + * and newer automatically control the fan speed, which would + * interfere with SW control. + */ + return (device->chipset >= 0xc0); +} + +void +nvkm_pmu_pgob(struct nvkm_pmu *pmu, bool enable) +{ + if (pmu && pmu->func->pgob) + pmu->func->pgob(pmu, enable); +} + +static void +nvkm_pmu_recv(struct work_struct *work) +{ + struct nvkm_pmu *pmu = container_of(work, typeof(*pmu), recv.work); + return pmu->func->recv(pmu); +} + +int +nvkm_pmu_send(struct nvkm_pmu *pmu, u32 reply[2], + u32 process, u32 message, u32 data0, u32 data1) +{ + if (!pmu || !pmu->func->send) + return -ENODEV; + return pmu->func->send(pmu, reply, process, message, data0, data1); +} + +static void +nvkm_pmu_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_pmu *pmu = nvkm_pmu(subdev); + if (!pmu->func->intr) + return; + pmu->func->intr(pmu); +} + +static int +nvkm_pmu_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_pmu *pmu = nvkm_pmu(subdev); + + if (pmu->func->fini) + pmu->func->fini(pmu); + + flush_work(&pmu->recv.work); + + reinit_completion(&pmu->wpr_ready); + + nvkm_falcon_cmdq_fini(pmu->lpq); + nvkm_falcon_cmdq_fini(pmu->hpq); + pmu->initmsg_received = false; + return 0; +} + +static void +nvkm_pmu_reset(struct nvkm_pmu *pmu) +{ + struct nvkm_device *device = pmu->subdev.device; + + if (!pmu->func->enabled(pmu)) + return; + + /* Reset. */ + if (pmu->func->reset) + pmu->func->reset(pmu); + + /* Wait for IMEM/DMEM scrubbing to be complete. */ + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x10a10c) & 0x00000006)) + break; + ); +} + +static int +nvkm_pmu_preinit(struct nvkm_subdev *subdev) +{ + struct nvkm_pmu *pmu = nvkm_pmu(subdev); + nvkm_pmu_reset(pmu); + return 0; +} + +static int +nvkm_pmu_init(struct nvkm_subdev *subdev) +{ + struct nvkm_pmu *pmu = nvkm_pmu(subdev); + struct nvkm_device *device = pmu->subdev.device; + + if (!pmu->func->init) + return 0; + + if (pmu->func->enabled(pmu)) { + /* Inhibit interrupts, and wait for idle. */ + nvkm_wr32(device, 0x10a014, 0x0000ffff); + nvkm_msec(device, 2000, + if (!nvkm_rd32(device, 0x10a04c)) + break; + ); + + nvkm_pmu_reset(pmu); + } + + return pmu->func->init(pmu); +} + +static void * +nvkm_pmu_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_pmu *pmu = nvkm_pmu(subdev); + nvkm_falcon_msgq_del(&pmu->msgq); + nvkm_falcon_cmdq_del(&pmu->lpq); + nvkm_falcon_cmdq_del(&pmu->hpq); + nvkm_falcon_qmgr_del(&pmu->qmgr); + nvkm_falcon_dtor(&pmu->falcon); + mutex_destroy(&pmu->send.mutex); + return nvkm_pmu(subdev); +} + +static const struct nvkm_subdev_func +nvkm_pmu = { + .dtor = nvkm_pmu_dtor, + .preinit = nvkm_pmu_preinit, + .init = nvkm_pmu_init, + .fini = nvkm_pmu_fini, + .intr = nvkm_pmu_intr, +}; + +int +nvkm_pmu_ctor(const struct nvkm_pmu_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_pmu *pmu) +{ + int ret; + + nvkm_subdev_ctor(&nvkm_pmu, device, type, inst, &pmu->subdev); + + mutex_init(&pmu->send.mutex); + + INIT_WORK(&pmu->recv.work, nvkm_pmu_recv); + init_waitqueue_head(&pmu->recv.wait); + + fwif = nvkm_firmware_load(&pmu->subdev, fwif, "Pmu", pmu); + if (IS_ERR(fwif)) + return PTR_ERR(fwif); + + pmu->func = fwif->func; + + ret = nvkm_falcon_ctor(pmu->func->flcn, &pmu->subdev, pmu->subdev.name, + 0x10a000, &pmu->falcon); + if (ret) + return ret; + + if ((ret = nvkm_falcon_qmgr_new(&pmu->falcon, &pmu->qmgr)) || + (ret = nvkm_falcon_cmdq_new(pmu->qmgr, "hpq", &pmu->hpq)) || + (ret = nvkm_falcon_cmdq_new(pmu->qmgr, "lpq", &pmu->lpq)) || + (ret = nvkm_falcon_msgq_new(pmu->qmgr, "msgq", &pmu->msgq))) + return ret; + + init_completion(&pmu->wpr_ready); + return 0; +} + +int +nvkm_pmu_new_(const struct nvkm_pmu_fwif *fwif, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_pmu **ppmu) +{ + struct nvkm_pmu *pmu; + if (!(pmu = *ppmu = kzalloc(sizeof(*pmu), GFP_KERNEL))) + return -ENOMEM; + return nvkm_pmu_ctor(fwif, device, type, inst, *ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc new file mode 100644 index 000000000..214a6d9e0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Martin Peres + * + * 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 folloing 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: Martin Peres + */ + +/****************************************************************************** + * arith data segment + *****************************************************************************/ +#ifdef INCLUDE_PROC +#endif + +#ifdef INCLUDE_DATA +#endif + +/****************************************************************************** + * arith code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE + +// does a 32x32 -> 64 multiplication +// +// A * B = A_lo * B_lo +// + ( A_hi * B_lo ) << 16 +// + ( A_lo * B_hi ) << 16 +// + ( A_hi * B_hi ) << 32 +// +// $r15 - current +// $r14 - A +// $r13 - B +// $r12 - mul_lo (return) +// $r11 - mul_hi (return) +// $r0 - zero +mulu32_32_64: + push $r1 // A_hi + push $r2 // B_hi + push $r3 // tmp0 + push $r4 // tmp1 + + shr b32 $r1 $r14 16 + shr b32 $r2 $r13 16 + + clear b32 $r12 + clear b32 $r11 + + // A_lo * B_lo + mulu $r12 $r14 $r13 + + // ( A_hi * B_lo ) << 16 + mulu $r3 $r1 $r13 // tmp0 = A_hi * B_lo + mov b32 $r4 $r3 + and $r3 0xffff // tmp0 = tmp0_lo + shl b32 $r3 16 + shr b32 $r4 16 // tmp1 = tmp0_hi + add b32 $r12 $r3 + adc b32 $r11 $r4 + + // ( A_lo * B_hi ) << 16 + mulu $r3 $r14 $r2 // tmp0 = A_lo * B_hi + mov b32 $r4 $r3 + and $r3 0xffff // tmp0 = tmp0_lo + shl b32 $r3 16 + shr b32 $r4 16 // tmp1 = tmp0_hi + add b32 $r12 $r3 + adc b32 $r11 $r4 + + // ( A_hi * B_hi ) << 32 + mulu $r3 $r1 $r2 // tmp0 = A_hi * B_hi + add b32 $r11 $r3 + + pop $r4 + pop $r3 + pop $r2 + pop $r1 + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 new file mode 100644 index 000000000..37e8407b7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 @@ -0,0 +1,70 @@ +/* + * 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 + */ + +#define NVKM_PPWR_CHIPSET GF100 +#define HW_TICKS_PER_US 203 // should be 202.5 + +//#define NVKM_FALCON_PC24 +//#define NVKM_FALCON_UNSHIFTED_IO +//#define NVKM_FALCON_MMIO_UAS +//#define NVKM_FALCON_MMIO_TRAP + +#include "macros.fuc" + +.section #gf100_pmu_data +#define INCLUDE_PROC +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_PROC + +#define INCLUDE_DATA +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_DATA +.align 256 + +.section #gf100_pmu_code +#define INCLUDE_CODE +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_CODE +.align 256 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h new file mode 100644 index 000000000..4cf888f2b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h @@ -0,0 +1,1864 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gf100_pmu_data[] = { +/* 0x0000: proc_kern */ + 0x52544e49, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: proc_list_head */ + 0x54534f48, + 0x0000050a, + 0x000004a7, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x00000754, + 0x00000746, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x00000758, + 0x00000756, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x00000b88, + 0x00000a2b, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000bb1, + 0x00000b8a, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000bbd, + 0x00000bbb, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0268: proc_list_tail */ +/* 0x0268: time_prev */ + 0x00000000, +/* 0x026c: time_next */ + 0x00000000, +/* 0x0270: fifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x02f0: rfifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0370: memx_func_head */ + 0x00000001, + 0x00000000, + 0x00000549, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x000005d3, + 0x00000003, + 0x00000002, + 0x0000069b, + 0x00040004, + 0x00000000, + 0x000006b7, + 0x00010005, + 0x00000000, + 0x000006d4, + 0x00010006, + 0x00000000, + 0x0000065b, + 0x00000007, + 0x00000000, + 0x000006df, +/* 0x03c4: memx_func_tail */ +/* 0x03c4: memx_ts_start */ + 0x00000000, +/* 0x03c8: memx_ts_end */ + 0x00000000, +/* 0x03cc: memx_data_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0bcc: memx_data_tail */ +/* 0x0bcc: memx_train_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0ccc: memx_train_tail */ +/* 0x0ccc: i2c_scl_map */ + 0x00001000, + 0x00004000, + 0x00010000, + 0x00000100, + 0x00040000, + 0x00100000, + 0x00400000, + 0x01000000, + 0x04000000, + 0x10000000, +/* 0x0cf4: i2c_sda_map */ + 0x00002000, + 0x00008000, + 0x00020000, + 0x00000200, + 0x00080000, + 0x00200000, + 0x00800000, + 0x02000000, + 0x08000000, + 0x20000000, +/* 0x0d1c: i2c_ctrl */ + 0x0000e138, + 0x0000e150, + 0x0000e168, + 0x0000e180, + 0x0000e254, + 0x0000e274, + 0x0000e764, + 0x0000e780, + 0x0000e79c, + 0x0000e7b8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gf100_pmu_code[] = { + 0x03920ef5, +/* 0x0004: rd32 */ + 0x07a007f1, + 0xd00604b6, + 0x04bd000e, + 0x0001d7f1, + 0xf101d3f0, + 0xb607ac07, + 0x0dd00604, +/* 0x0023: rd32_wait */ + 0xf104bd00, + 0xb607acd7, + 0xddcf06d4, + 0x00d4f100, + 0xf21bf470, + 0x07a4d7f1, + 0xcf06d4b6, + 0x00f800dd, +/* 0x0040: wr32 */ + 0x07a007f1, + 0xd00604b6, + 0x04bd000e, + 0x07a407f1, + 0xd00604b6, + 0x04bd000d, + 0x00f2d7f1, + 0xf101d3f0, + 0xb607ac07, + 0x0dd00604, +/* 0x006b: wr32_wait */ + 0xf104bd00, + 0xb607acd7, + 0xddcf06d4, + 0x00d4f100, + 0xf21bf470, +/* 0x007e: nsec */ + 0x90f900f8, + 0x87f080f9, + 0x0684b62c, +/* 0x008b: nsec_loop */ + 0xf00088cf, + 0x94b62c97, + 0x0099cf06, + 0xb80298bb, + 0x1ef4069e, + 0xfc80fcf1, +/* 0x00a3: wait */ + 0xf900f890, + 0xf080f990, + 0x84b62c87, + 0x0088cf06, +/* 0x00b0: wait_loop */ + 0xf402eeb9, + 0xdab90421, + 0x04adfd02, + 0xf406acb8, + 0x97f0150b, + 0x0694b62c, + 0xbb0099cf, + 0x9bb80298, + 0xdf1ef406, +/* 0x00d4: wait_done */ + 0x90fc80fc, +/* 0x00da: intr_watchdog */ + 0xe99800f8, + 0x0096b003, + 0x982a0bf4, + 0x9abb9a0a, + 0x0f1cf402, + 0xf501d7f0, + 0xbd02d121, + 0x150ef494, +/* 0x00f8: intr_watchdog_next_time */ + 0xb09b0a98, + 0x0bf400a6, + 0x069ab809, +/* 0x0107: intr_watchdog_next_time_set */ + 0x80061cf4, +/* 0x010a: intr_watchdog_next_proc */ + 0xe9809b09, + 0x58e0b603, + 0x0268e6b1, + 0xf8c61bf4, +/* 0x0119: intr */ + 0xbd00f900, + 0xf980f904, + 0xf9a0f990, + 0xf9c0f9b0, + 0xf9e0f9d0, + 0x00f7f0f0, + 0xf90188fe, + 0xd087f180, + 0x0684b605, + 0xb60088cf, + 0x07f10180, + 0x04b605d0, + 0x0008d006, + 0x87f004bd, + 0x0684b608, + 0xc40088cf, + 0x0bf40289, + 0x9b008023, + 0xf458e7f0, + 0x0998da21, + 0x0096b09b, + 0xf0110bf4, + 0x04b63407, + 0x0009d006, + 0x098004bd, +/* 0x017d: intr_skip_watchdog */ + 0x0089e49a, + 0x480bf408, + 0x068897f1, + 0xcf0694b6, + 0x9ac40099, + 0x2c0bf402, + 0x04c0c7f1, + 0xcf06c4b6, + 0xc0f900cc, + 0x4f48e7f1, + 0x5453e3f1, + 0xf500d7f0, + 0xfc033621, + 0xc007f1c0, + 0x0604b604, + 0xbd000cd0, +/* 0x01bd: intr_subintr_skip_fifo */ + 0x8807f104, + 0x0604b606, + 0xbd0009d0, +/* 0x01c9: intr_skip_subintr */ + 0xe097f104, + 0xfd90bd00, + 0x07f00489, + 0x0604b604, + 0xbd0008d0, + 0xfe80fc04, + 0xf0fc0088, + 0xd0fce0fc, + 0xb0fcc0fc, + 0x90fca0fc, + 0x00fc80fc, + 0xf80032f4, +/* 0x01f9: ticks_from_ns */ + 0xf9c0f901, + 0xcbd7f1b0, + 0x00d3f000, + 0x040b21f5, + 0x03e8ccec, + 0xf400b4b0, + 0xeeec120b, + 0xd7f103e8, + 0xd3f000cb, + 0x0b21f500, +/* 0x0221: ticks_from_ns_quit */ + 0x02ceb904, + 0xc0fcb0fc, +/* 0x022a: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f000cb, + 0x0b21f500, + 0x02ceb904, + 0xf400b4b0, + 0xe4bd050b, +/* 0x0244: ticks_from_us_quit */ + 0xc0fcb0fc, +/* 0x024a: ticks_to_us */ + 0xd7f100f8, + 0xd3f000cb, + 0xecedff00, +/* 0x0256: timer */ + 0x90f900f8, + 0x32f480f9, + 0x03f89810, + 0xf40086b0, + 0x84bd651c, + 0xb63807f0, + 0x08d00604, + 0xf004bd00, + 0x84b63487, + 0x0088cf06, + 0xbb9a0998, + 0xe9bb0298, + 0x03fe8000, + 0xb60887f0, + 0x88cf0684, + 0x0284f000, + 0xf0261bf4, + 0x84b63487, + 0x0088cf06, + 0xf406e0b8, + 0xe8b8090b, + 0x111cf406, +/* 0x02ac: timer_reset */ + 0xb63407f0, + 0x0ed00604, + 0x8004bd00, +/* 0x02ba: timer_enable */ + 0x87f09a0e, + 0x3807f001, + 0xd00604b6, + 0x04bd0008, +/* 0x02c8: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x02d1: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89b80486, + 0x2a0bf406, + 0x940398c4, + 0x80b60488, + 0x008ebb18, + 0x8000fa98, + 0x8d80008a, + 0x028c8001, + 0xb6038b80, + 0x94f00190, + 0x04e98007, +/* 0x030b: send_done */ + 0xfc0231f4, + 0xf880fc90, +/* 0x0311: find */ + 0xf080f900, + 0x31f45887, +/* 0x0319: find_loop */ + 0x008a9801, + 0xf406aeb8, + 0x80b6100b, + 0x6886b158, + 0xf01bf402, +/* 0x032f: find_done */ + 0xb90132f4, + 0x80fc028e, +/* 0x0336: send */ + 0x21f500f8, + 0x01f40311, +/* 0x033f: recv */ + 0xf900f897, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0xf40689b8, + 0x89c43d0b, + 0x0180b603, + 0x800784f0, + 0xea9805e8, + 0xfef0f902, + 0xf0f9018f, + 0x9402efb9, + 0xe9bb0499, + 0x18e0b600, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0xf0fca5f9, + 0xf400f8fe, + 0xf0fc0131, +/* 0x038c: recv_done */ + 0x90fc80fc, +/* 0x0392: init */ + 0x17f100f8, + 0x14b60108, + 0x0011cf06, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xd00604b6, + 0x04bd0001, + 0xf0ff17f0, + 0x04b61407, + 0x0001d006, + 0x17f004bd, + 0x0015f102, + 0x1007f008, + 0xd00604b6, + 0x04bd0001, + 0x011917f1, + 0xf10013f0, + 0xfeffff14, + 0x31f40010, + 0x0117f010, + 0xb63807f0, + 0x01d00604, + 0xf004bd00, +/* 0x03fa: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x040b: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x045c: host_send */ + 0x04b017f1, + 0xcf0614b6, + 0x27f10011, + 0x24b604a0, + 0x0022cf06, + 0xf40612b8, + 0x1ec4320b, + 0x04ee9407, + 0x0270e0b7, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0x033621f5, + 0xc40110b6, + 0x07f10f1e, + 0x04b604b0, + 0x000ed006, + 0x0ef404bd, +/* 0x04a5: host_send_done */ +/* 0x04a7: host_recv */ + 0xf100f8ba, + 0xf14e4917, + 0xb8525413, + 0x0bf406e1, +/* 0x04b5: host_recv_wait */ + 0xcc17f1aa, + 0x0614b604, + 0xf10011cf, + 0xb604c827, + 0x22cf0624, + 0x0816f000, + 0xf40612b8, + 0x23c4e60b, + 0x0434b607, + 0x02f030b7, + 0x80033b80, + 0x3d80023c, + 0x003e8001, + 0xf00120b6, + 0x07f10f24, + 0x04b604c8, + 0x0002d006, + 0x27f004bd, + 0x0007f040, + 0xd00604b6, + 0x04bd0002, +/* 0x050a: host_init */ + 0x17f100f8, + 0x14b60080, + 0x7015f110, + 0xd007f102, + 0x0604b604, + 0xbd0001d0, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xd00604b6, + 0x04bd0001, + 0xf10117f0, + 0xb604c407, + 0x01d00604, + 0xf804bd00, +/* 0x0549: memx_func_enter */ + 0x2067f100, + 0x5d77f116, + 0xff73f1f5, + 0x026eb9ff, + 0xb90421f4, + 0x87fd02d8, + 0xf960f904, + 0xfcd0fc80, + 0x4021f4e0, + 0xfffe77f1, + 0xffff73f1, + 0xf4026eb9, + 0xd8b90421, + 0x0487fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf14021f4, + 0xb926f067, + 0x21f4026e, + 0x02d8b904, + 0xf90487fd, + 0xfc80f960, + 0xf4e0fcd0, + 0x67f04021, + 0xe007f104, + 0x0604b607, + 0xbd0006d0, +/* 0x05b5: memx_func_enter_wait */ + 0xc067f104, + 0x0664b607, + 0xf00066cf, + 0x0bf40464, + 0x2c67f0f3, + 0xcf0664b6, + 0x06800066, +/* 0x05d3: memx_func_leave */ + 0xf000f8f1, + 0x64b62c67, + 0x0066cf06, + 0xf0f20680, + 0x07f10467, + 0x04b607e4, + 0x0006d006, +/* 0x05ee: memx_func_leave_wait */ + 0x67f104bd, + 0x64b607c0, + 0x0066cf06, + 0xf40464f0, + 0x67f1f31b, + 0x77f126f0, + 0x73f00001, + 0x026eb900, + 0xb90421f4, + 0x87fd02d8, + 0xf960f905, + 0xfcd0fc80, + 0x4021f4e0, + 0x162067f1, + 0xf4026eb9, + 0xd8b90421, + 0x0587fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf14021f4, + 0xf00aa277, + 0x6eb90073, + 0x0421f402, + 0xfd02d8b9, + 0x60f90587, + 0xd0fc80f9, + 0x21f4e0fc, +/* 0x065b: memx_func_wait_vblank */ + 0x9800f840, + 0x66b00016, + 0x120bf400, + 0xf40166b0, + 0x0ef4060b, +/* 0x066d: memx_func_wait_vblank_head1 */ + 0x2077f02c, +/* 0x0673: memx_func_wait_vblank_head0 */ + 0xf0060ef4, +/* 0x0676: memx_func_wait_vblank_0 */ + 0x67f10877, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x0686: memx_func_wait_vblank_1 */ + 0x67f1f31b, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x0696: memx_func_wait_vblank_fini */ + 0x10b6f30b, +/* 0x069b: memx_func_wr32 */ + 0x9800f804, + 0x15980016, + 0x0810b601, + 0x50f960f9, + 0xe0fcd0fc, + 0xb64021f4, + 0x1bf40242, +/* 0x06b7: memx_func_wait */ + 0xf000f8e9, + 0x84b62c87, + 0x0088cf06, + 0x98001e98, + 0x1c98011d, + 0x031b9802, + 0xf41010b6, + 0x00f8a321, +/* 0x06d4: memx_func_delay */ + 0xb6001e98, + 0x21f40410, +/* 0x06df: memx_func_train */ + 0xf800f87e, +/* 0x06e1: memx_exec */ + 0xf9e0f900, + 0x02c1b9d0, +/* 0x06eb: memx_exec_next */ + 0x9802b2b9, + 0x10b60013, + 0xf034e704, + 0xe033e701, + 0x0132b601, + 0x980c30f0, + 0x55f9de35, + 0xf40612b8, + 0x0b98e41e, + 0xf20c98f1, + 0xf102cbbb, + 0xb607c4b7, + 0xbbcf06b4, + 0xfcd0fc00, + 0x3621f5e0, +/* 0x0727: memx_info */ + 0x7000f803, + 0x0bf401c6, +/* 0x072d: memx_info_data */ + 0xccc7f10e, + 0x00b7f103, + 0x0b0ef408, +/* 0x0738: memx_info_train */ + 0x0bccc7f1, + 0x0100b7f1, +/* 0x0740: memx_info_send */ + 0x033621f5, +/* 0x0746: memx_recv */ + 0xd6b000f8, + 0x980bf401, + 0xf400d6b0, + 0x00f8d80b, +/* 0x0754: memx_init */ +/* 0x0756: perf_recv */ + 0x00f800f8, +/* 0x0758: perf_init */ +/* 0x075a: i2c_drive_scl */ + 0x36b000f8, + 0x110bf400, + 0x07e007f1, + 0xd00604b6, + 0x04bd0001, +/* 0x076e: i2c_drive_scl_lo */ + 0x07f100f8, + 0x04b607e4, + 0x0001d006, + 0x00f804bd, +/* 0x077c: i2c_drive_sda */ + 0xf40036b0, + 0x07f1110b, + 0x04b607e0, + 0x0002d006, + 0x00f804bd, +/* 0x0790: i2c_drive_sda_lo */ + 0x07e407f1, + 0xd00604b6, + 0x04bd0002, +/* 0x079e: i2c_sense_scl */ + 0x32f400f8, + 0xc437f101, + 0x0634b607, + 0xfd0033cf, + 0x0bf40431, + 0x0131f406, +/* 0x07b4: i2c_sense_scl_done */ +/* 0x07b6: i2c_sense_sda */ + 0x32f400f8, + 0xc437f101, + 0x0634b607, + 0xfd0033cf, + 0x0bf40432, + 0x0131f406, +/* 0x07cc: i2c_sense_sda_done */ +/* 0x07ce: i2c_raise_scl */ + 0x40f900f8, + 0x089847f1, + 0xf50137f0, +/* 0x07db: i2c_raise_scl_wait */ + 0xf1075a21, + 0xf403e8e7, + 0x21f57e21, + 0x01f4079e, + 0x0142b609, +/* 0x07ef: i2c_raise_scl_done */ + 0xfcef1bf4, +/* 0x07f3: i2c_start */ + 0xf500f840, + 0xf4079e21, + 0x21f50d11, + 0x11f407b6, + 0x300ef406, +/* 0x0804: i2c_start_rep */ + 0xf50037f0, + 0xf0075a21, + 0x21f50137, + 0x76bb077c, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb607ce21, + 0x11f40464, +/* 0x0831: i2c_start_send */ + 0x0037f01f, + 0x077c21f5, + 0x1388e7f1, + 0xf07e21f4, + 0x21f50037, + 0xe7f1075a, + 0x21f41388, +/* 0x084d: i2c_start_out */ +/* 0x084f: i2c_stop */ + 0xf000f87e, + 0x21f50037, + 0x37f0075a, + 0x7c21f500, + 0xe8e7f107, + 0x7e21f403, + 0xf50137f0, + 0xf1075a21, + 0xf41388e7, + 0x37f07e21, + 0x7c21f501, + 0x88e7f107, + 0x7e21f413, +/* 0x0882: i2c_bitw */ + 0x21f500f8, + 0xe7f1077c, + 0x21f403e8, + 0x0076bb7e, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b607ce, + 0x1811f404, + 0x1388e7f1, + 0xf07e21f4, + 0x21f50037, + 0xe7f1075a, + 0x21f41388, +/* 0x08c1: i2c_bitw_out */ +/* 0x08c3: i2c_bitr */ + 0xf000f87e, + 0x21f50137, + 0xe7f1077c, + 0x21f403e8, + 0x0076bb7e, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b607ce, + 0x1b11f404, + 0x07b621f5, + 0xf50037f0, + 0xf1075a21, + 0xf41388e7, + 0x3cf07e21, + 0x0131f401, +/* 0x0908: i2c_bitr_done */ +/* 0x090a: i2c_get_byte */ + 0x57f000f8, + 0x0847f000, +/* 0x0910: i2c_get_byte_next */ + 0xbb0154b6, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x08c321f5, + 0xf40464b6, + 0x53fd2b11, + 0x0142b605, + 0xf0d81bf4, + 0x76bb0137, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6088221, +/* 0x095a: i2c_get_byte_done */ + 0x00f80464, +/* 0x095c: i2c_put_byte */ +/* 0x095f: i2c_put_byte_next */ + 0xb60847f0, + 0x54ff0142, + 0x0076bb38, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60882, + 0x3411f404, + 0xf40046b0, + 0x76bbd81b, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb608c321, + 0x11f40464, + 0x0076bb0f, + 0xf40136b0, + 0x32f4061b, +/* 0x09b5: i2c_put_byte_done */ +/* 0x09b7: i2c_addr */ + 0xbb00f801, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x07f321f5, + 0xf40464b6, + 0xc3e72911, + 0x34b6012e, + 0x0553fd01, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x5c21f550, + 0x0464b609, +/* 0x09fc: i2c_addr_done */ +/* 0x09fe: i2c_acquire_addr */ + 0xcec700f8, + 0x02e4b6f8, + 0x0d1ce0b7, + 0xf800ee98, +/* 0x0a0d: i2c_acquire */ + 0xfe21f500, + 0x0421f409, + 0xf403d9f0, + 0x00f84021, +/* 0x0a1c: i2c_release */ + 0x09fe21f5, + 0xf00421f4, + 0x21f403da, +/* 0x0a2b: i2c_recv */ + 0xf400f840, + 0xc1c70132, + 0x0214b6f8, + 0xf52816b0, + 0xa0013a1f, + 0x980cf413, + 0x13a00032, + 0x31980ccc, + 0x0231f400, + 0xe0f9d0f9, + 0x67f1d0f9, + 0x63f10000, + 0x67921000, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60a0d, + 0xb0d0fc04, + 0x1bf500d6, + 0x57f000b3, + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b609b7, + 0xd011f504, + 0xe0c5c700, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x5c21f550, + 0x0464b609, + 0x00ad11f5, + 0xbb0157f0, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x09b721f5, + 0xf50464b6, + 0xbb008a11, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x090a21f5, + 0xf40464b6, + 0x5bcb6a11, + 0x0076bbe0, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b6084f, + 0x025bb904, + 0x0ef474bd, +/* 0x0b31: i2c_recv_not_rd08 */ + 0x01d6b043, + 0xf03d1bf4, + 0x21f50057, + 0x11f409b7, + 0xe0c5c733, + 0x095c21f5, + 0xf02911f4, + 0x21f50057, + 0x11f409b7, + 0xe0b5c71f, + 0x095c21f5, + 0xf51511f4, + 0xbd084f21, + 0x08c5c774, + 0xf4091bf4, + 0x0ef40232, +/* 0x0b71: i2c_recv_not_wr08 */ +/* 0x0b71: i2c_recv_done */ + 0xf8cec703, + 0x0a1c21f5, + 0xd0fce0fc, + 0xb90a12f4, + 0x21f5027c, +/* 0x0b86: i2c_recv_exit */ + 0x00f80336, +/* 0x0b88: i2c_init */ +/* 0x0b8a: test_recv */ + 0x17f100f8, + 0x14b605d8, + 0x0011cf06, + 0xf10110b6, + 0xb605d807, + 0x01d00604, + 0xf104bd00, + 0xf1d900e7, + 0xf5134fe3, + 0xf8025621, +/* 0x0bb1: test_init */ + 0x00e7f100, + 0x5621f508, +/* 0x0bbb: idle_recv */ + 0xf800f802, +/* 0x0bbd: idle */ + 0x0031f400, + 0x05d417f1, + 0xcf0614b6, + 0x10b60011, + 0xd407f101, + 0x0604b605, + 0xbd0001d0, +/* 0x0bd9: idle_loop */ + 0x5817f004, +/* 0x0bdf: idle_proc */ +/* 0x0bdf: idle_proc_exec */ + 0xf90232f4, + 0x021eb910, + 0x033f21f5, + 0x11f410fc, + 0x0231f409, +/* 0x0bf3: idle_proc_next */ + 0xb6ef0ef4, + 0x1fb85810, + 0xe61bf406, + 0xf4dd02f4, + 0x0ef40028, + 0x000000bb, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4 new file mode 100644 index 000000000..2f28c7e26 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4 @@ -0,0 +1,70 @@ +/* + * 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 + */ + +#define NVKM_PPWR_CHIPSET GF119 +#define HW_TICKS_PER_US 324 + +//#define NVKM_FALCON_PC24 +#define NVKM_FALCON_UNSHIFTED_IO +//#define NVKM_FALCON_MMIO_UAS +//#define NVKM_FALCON_MMIO_TRAP + +#include "macros.fuc" + +.section #gf119_pmu_data +#define INCLUDE_PROC +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_PROC + +#define INCLUDE_DATA +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_DATA +.align 256 + +.section #gf119_pmu_code +#define INCLUDE_CODE +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_CODE +.align 256 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4.h new file mode 100644 index 000000000..e80eff18e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf119.fuc4.h @@ -0,0 +1,1794 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gf119_pmu_data[] = { +/* 0x0000: proc_kern */ + 0x52544e49, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: proc_list_head */ + 0x54534f48, + 0x00000495, + 0x0000043e, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x00000683, + 0x00000675, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x00000687, + 0x00000685, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x00000aa2, + 0x00000945, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000ac5, + 0x00000aa4, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000ad1, + 0x00000acf, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0268: proc_list_tail */ +/* 0x0268: time_prev */ + 0x00000000, +/* 0x026c: time_next */ + 0x00000000, +/* 0x0270: fifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x02f0: rfifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0370: memx_func_head */ + 0x00000001, + 0x00000000, + 0x000004cb, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x0000054c, + 0x00000003, + 0x00000002, + 0x000005d0, + 0x00040004, + 0x00000000, + 0x000005ec, + 0x00010005, + 0x00000000, + 0x00000606, + 0x00010006, + 0x00000000, + 0x000005cb, + 0x00000007, + 0x00000000, + 0x00000611, +/* 0x03c4: memx_func_tail */ +/* 0x03c4: memx_ts_start */ + 0x00000000, +/* 0x03c8: memx_ts_end */ + 0x00000000, +/* 0x03cc: memx_data_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0bcc: memx_data_tail */ +/* 0x0bcc: memx_train_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0ccc: memx_train_tail */ +/* 0x0ccc: i2c_scl_map */ + 0x00000400, + 0x00000800, + 0x00001000, + 0x00002000, + 0x00004000, + 0x00008000, + 0x00010000, + 0x00020000, + 0x00040000, + 0x00080000, +/* 0x0cf4: i2c_sda_map */ + 0x00100000, + 0x00200000, + 0x00400000, + 0x00800000, + 0x01000000, + 0x02000000, + 0x04000000, + 0x08000000, + 0x10000000, + 0x20000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gf119_pmu_code[] = { + 0x03410ef5, +/* 0x0004: rd32 */ + 0x07a007f1, + 0xbd000ed0, + 0x01d7f104, + 0x01d3f000, + 0x07ac07f1, + 0xbd000dd0, +/* 0x001d: rd32_wait */ + 0xacd7f104, + 0x00ddcf07, + 0x7000d4f1, + 0xf1f51bf4, + 0xcf07a4d7, + 0x00f800dd, +/* 0x0034: wr32 */ + 0x07a007f1, + 0xbd000ed0, + 0xa407f104, + 0x000dd007, + 0xd7f104bd, + 0xd3f000f2, + 0xac07f101, + 0x000dd007, +/* 0x0056: wr32_wait */ + 0xd7f104bd, + 0xddcf07ac, + 0x00d4f100, + 0xf51bf470, +/* 0x0066: nsec */ + 0x90f900f8, + 0x87f080f9, + 0x0088cf2c, +/* 0x0070: nsec_loop */ + 0xcf2c97f0, + 0x98bb0099, + 0x069eb802, + 0xfcf41ef4, + 0xf890fc80, +/* 0x0085: wait */ + 0xf990f900, + 0x2c87f080, +/* 0x008f: wait_loop */ + 0xb90088cf, + 0x21f402ee, + 0x02dab904, + 0xb804adfd, + 0x0bf406ac, + 0x2c97f012, + 0xbb0099cf, + 0x9bb80298, + 0xe21ef406, +/* 0x00b0: wait_done */ + 0x90fc80fc, +/* 0x00b6: intr_watchdog */ + 0xe99800f8, + 0x0096b003, + 0x982a0bf4, + 0x9abb9a0a, + 0x0f1cf402, + 0xf501d7f0, + 0xbd028021, + 0x150ef494, +/* 0x00d4: intr_watchdog_next_time */ + 0xb09b0a98, + 0x0bf400a6, + 0x069ab809, +/* 0x00e3: intr_watchdog_next_time_set */ + 0x80061cf4, +/* 0x00e6: intr_watchdog_next_proc */ + 0xe9809b09, + 0x58e0b603, + 0x0268e6b1, + 0xf8c61bf4, +/* 0x00f5: intr */ + 0xbd00f900, + 0xf980f904, + 0xf9a0f990, + 0xf9c0f9b0, + 0xf9e0f9d0, + 0x00f7f0f0, + 0xf90188fe, + 0xd087f180, + 0x0088cf05, + 0xf10180b6, + 0xd005d007, + 0x04bd0008, + 0xcf0887f0, + 0x89c40088, + 0x200bf402, + 0xf09b0080, + 0x21f458e7, + 0x9b0998b6, + 0xf40096b0, + 0x07f00e0b, + 0x0009d034, + 0x098004bd, +/* 0x014d: intr_skip_watchdog */ + 0x0089e49a, + 0x3c0bf408, + 0x068897f1, + 0xc40099cf, + 0x0bf4029a, + 0xc0c7f126, + 0x00cccf04, + 0xe7f1c0f9, + 0xe3f14f48, + 0xd7f05453, + 0xe521f500, + 0xf1c0fc02, + 0xd004c007, + 0x04bd000c, +/* 0x0184: intr_subintr_skip_fifo */ + 0x068807f1, + 0xbd0009d0, +/* 0x018d: intr_skip_subintr */ + 0xe097f104, + 0xfd90bd00, + 0x07f00489, + 0x0008d004, + 0x80fc04bd, + 0xfc0088fe, + 0xfce0fcf0, + 0xfcc0fcd0, + 0xfca0fcb0, + 0xfc80fc90, + 0x0032f400, +/* 0x01ba: ticks_from_ns */ + 0xc0f901f8, + 0xd7f1b0f9, + 0xd3f00144, + 0xab21f500, + 0xe8ccec03, + 0x00b4b003, + 0xec120bf4, + 0xf103e8ee, + 0xf00144d7, + 0x21f500d3, +/* 0x01e2: ticks_from_ns_quit */ + 0xceb903ab, + 0xfcb0fc02, +/* 0x01eb: ticks_from_us */ + 0xf900f8c0, + 0xf1b0f9c0, + 0xf00144d7, + 0x21f500d3, + 0xceb903ab, + 0x00b4b002, + 0xbd050bf4, +/* 0x0205: ticks_from_us_quit */ + 0xfcb0fce4, +/* 0x020b: ticks_to_us */ + 0xf100f8c0, + 0xf00144d7, + 0xedff00d3, +/* 0x0217: timer */ + 0xf900f8ec, + 0xf480f990, + 0xf8981032, + 0x0086b003, + 0xbd531cf4, + 0x3807f084, + 0xbd0008d0, + 0x3487f004, + 0x980088cf, + 0x98bb9a09, + 0x00e9bb02, + 0xf003fe80, + 0x88cf0887, + 0x0284f000, + 0xf0201bf4, + 0x88cf3487, + 0x06e0b800, + 0xb8090bf4, + 0x1cf406e8, +/* 0x0261: timer_reset */ + 0x3407f00e, + 0xbd000ed0, + 0x9a0e8004, +/* 0x026c: timer_enable */ + 0xf00187f0, + 0x08d03807, +/* 0x0277: timer_done */ + 0xf404bd00, + 0x80fc1031, + 0x00f890fc, +/* 0x0280: send_proc */ + 0x90f980f9, + 0x9805e898, + 0x86f004e9, + 0x0689b804, + 0xc42a0bf4, + 0x88940398, + 0x1880b604, + 0x98008ebb, + 0x8a8000fa, + 0x018d8000, + 0x80028c80, + 0x90b6038b, + 0x0794f001, + 0xf404e980, +/* 0x02ba: send_done */ + 0x90fc0231, + 0x00f880fc, +/* 0x02c0: find */ + 0x87f080f9, + 0x0131f458, +/* 0x02c8: find_loop */ + 0xb8008a98, + 0x0bf406ae, + 0x5880b610, + 0x026886b1, + 0xf4f01bf4, +/* 0x02de: find_done */ + 0x8eb90132, + 0xf880fc02, +/* 0x02e5: send */ + 0xc021f500, + 0x9701f402, +/* 0x02ee: recv */ + 0x90f900f8, + 0xe89880f9, + 0x04e99805, + 0xb80132f4, + 0x0bf40689, + 0x0389c43d, + 0xf00180b6, + 0xe8800784, + 0x02ea9805, + 0x8ffef0f9, + 0xb9f0f901, + 0x999402ef, + 0x00e9bb04, + 0x9818e0b6, + 0xec9803eb, + 0x01ed9802, + 0xf900ee98, + 0xfef0fca5, + 0x31f400f8, +/* 0x033b: recv_done */ + 0xfcf0fc01, + 0xf890fc80, +/* 0x0341: init */ + 0x0817f100, + 0x0011cf01, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xbd0001d0, + 0xff17f004, + 0xd01407f0, + 0x04bd0001, + 0xf10217f0, + 0xf0080015, + 0x01d01007, + 0xf104bd00, + 0xf000f517, + 0x14f10013, + 0x10feffff, + 0x1031f400, + 0xf00117f0, + 0x01d03807, + 0xf004bd00, +/* 0x039a: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x03ab: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x03fc: host_send */ + 0x04b017f1, + 0xf10011cf, + 0xcf04a027, + 0x12b80022, + 0x2f0bf406, + 0x94071ec4, + 0xe0b704ee, + 0xeb980270, + 0x02ec9803, + 0x9801ed98, + 0x21f500ee, + 0x10b602e5, + 0x0f1ec401, + 0x04b007f1, + 0xbd000ed0, + 0xc30ef404, +/* 0x043c: host_send_done */ +/* 0x043e: host_recv */ + 0x17f100f8, + 0x13f14e49, + 0xe1b85254, + 0xb30bf406, +/* 0x044c: host_recv_wait */ + 0x04cc17f1, + 0xf10011cf, + 0xcf04c827, + 0x16f00022, + 0x0612b808, + 0xc4ec0bf4, + 0x34b60723, + 0xf030b704, + 0x033b8002, + 0x80023c80, + 0x3e80013d, + 0x0120b600, + 0xf10f24f0, + 0xd004c807, + 0x04bd0002, + 0xf04027f0, + 0x02d00007, + 0xf804bd00, +/* 0x0495: host_init */ + 0x8017f100, + 0x1014b600, + 0x027015f1, + 0x04d007f1, + 0xbd0001d0, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xbd0001d0, + 0x0117f004, + 0x04c407f1, + 0xbd0001d0, +/* 0x04cb: memx_func_enter */ + 0xf100f804, + 0xf1162067, + 0xf1f55d77, + 0xb9ffff73, + 0x21f4026e, + 0x02d8b904, + 0xf90487fd, + 0xfc80f960, + 0xf4e0fcd0, + 0x77f13421, + 0x73f1fffe, + 0x6eb9ffff, + 0x0421f402, + 0xfd02d8b9, + 0x60f90487, + 0xd0fc80f9, + 0x21f4e0fc, + 0xf067f134, + 0x026eb926, + 0xb90421f4, + 0x87fd02d8, + 0xf960f904, + 0xfcd0fc80, + 0x3421f4e0, + 0xf10467f0, + 0xd007e007, + 0x04bd0006, +/* 0x0534: memx_func_enter_wait */ + 0x07c067f1, + 0xf00066cf, + 0x0bf40464, + 0x2c67f0f6, + 0x800066cf, + 0x00f8f106, +/* 0x054c: memx_func_leave */ + 0xcf2c67f0, + 0x06800066, + 0x0467f0f2, + 0x07e407f1, + 0xbd0006d0, +/* 0x0561: memx_func_leave_wait */ + 0xc067f104, + 0x0066cf07, + 0xf40464f0, + 0x67f1f61b, + 0x77f126f0, + 0x73f00001, + 0x026eb900, + 0xb90421f4, + 0x87fd02d8, + 0xf960f905, + 0xfcd0fc80, + 0x3421f4e0, + 0x162067f1, + 0xf4026eb9, + 0xd8b90421, + 0x0587fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf13421f4, + 0xf00aa277, + 0x6eb90073, + 0x0421f402, + 0xfd02d8b9, + 0x60f90587, + 0xd0fc80f9, + 0x21f4e0fc, +/* 0x05cb: memx_func_wait_vblank */ + 0xb600f834, + 0x00f80410, +/* 0x05d0: memx_func_wr32 */ + 0x98001698, + 0x10b60115, + 0xf960f908, + 0xfcd0fc50, + 0x3421f4e0, + 0xf40242b6, + 0x00f8e91b, +/* 0x05ec: memx_func_wait */ + 0xcf2c87f0, + 0x1e980088, + 0x011d9800, + 0x98021c98, + 0x10b6031b, + 0x8521f410, +/* 0x0606: memx_func_delay */ + 0x1e9800f8, + 0x0410b600, + 0xf86621f4, +/* 0x0611: memx_func_train */ +/* 0x0613: memx_exec */ + 0xf900f800, + 0xb9d0f9e0, + 0xb2b902c1, +/* 0x061d: memx_exec_next */ + 0x00139802, + 0xe70410b6, + 0xe701f034, + 0xb601e033, + 0x30f00132, + 0xde35980c, + 0x12b855f9, + 0xe41ef406, + 0x98f10b98, + 0xcbbbf20c, + 0xc4b7f102, + 0x00bbcf07, + 0xe0fcd0fc, + 0x02e521f5, +/* 0x0656: memx_info */ + 0xc67000f8, + 0x0e0bf401, +/* 0x065c: memx_info_data */ + 0x03ccc7f1, + 0x0800b7f1, +/* 0x0667: memx_info_train */ + 0xf10b0ef4, + 0xf10bccc7, +/* 0x066f: memx_info_send */ + 0xf50100b7, + 0xf802e521, +/* 0x0675: memx_recv */ + 0x01d6b000, + 0xb09b0bf4, + 0x0bf400d6, +/* 0x0683: memx_init */ + 0xf800f8d8, +/* 0x0685: perf_recv */ +/* 0x0687: perf_init */ + 0xf800f800, +/* 0x0689: i2c_drive_scl */ + 0x0036b000, + 0xf10e0bf4, + 0xd007e007, + 0x04bd0001, +/* 0x069a: i2c_drive_scl_lo */ + 0x07f100f8, + 0x01d007e4, + 0xf804bd00, +/* 0x06a5: i2c_drive_sda */ + 0x0036b000, + 0xf10e0bf4, + 0xd007e007, + 0x04bd0002, +/* 0x06b6: i2c_drive_sda_lo */ + 0x07f100f8, + 0x02d007e4, + 0xf804bd00, +/* 0x06c1: i2c_sense_scl */ + 0x0132f400, + 0x07c437f1, + 0xfd0033cf, + 0x0bf40431, + 0x0131f406, +/* 0x06d4: i2c_sense_scl_done */ +/* 0x06d6: i2c_sense_sda */ + 0x32f400f8, + 0xc437f101, + 0x0033cf07, + 0xf40432fd, + 0x31f4060b, +/* 0x06e9: i2c_sense_sda_done */ +/* 0x06eb: i2c_raise_scl */ + 0xf900f801, + 0x9847f140, + 0x0137f008, + 0x068921f5, +/* 0x06f8: i2c_raise_scl_wait */ + 0x03e8e7f1, + 0xf56621f4, + 0xf406c121, + 0x42b60901, + 0xef1bf401, +/* 0x070c: i2c_raise_scl_done */ + 0x00f840fc, +/* 0x0710: i2c_start */ + 0x06c121f5, + 0xf50d11f4, + 0xf406d621, + 0x0ef40611, +/* 0x0721: i2c_start_rep */ + 0x0037f030, + 0x068921f5, + 0xf50137f0, + 0xbb06a521, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x06eb21f5, + 0xf40464b6, +/* 0x074e: i2c_start_send */ + 0x37f01f11, + 0xa521f500, + 0x88e7f106, + 0x6621f413, + 0xf50037f0, + 0xf1068921, + 0xf41388e7, +/* 0x076a: i2c_start_out */ + 0x00f86621, +/* 0x076c: i2c_stop */ + 0xf50037f0, + 0xf0068921, + 0x21f50037, + 0xe7f106a5, + 0x21f403e8, + 0x0137f066, + 0x068921f5, + 0x1388e7f1, + 0xf06621f4, + 0x21f50137, + 0xe7f106a5, + 0x21f41388, +/* 0x079f: i2c_bitw */ + 0xf500f866, + 0xf106a521, + 0xf403e8e7, + 0x76bb6621, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb606eb21, + 0x11f40464, + 0x88e7f118, + 0x6621f413, + 0xf50037f0, + 0xf1068921, + 0xf41388e7, +/* 0x07de: i2c_bitw_out */ + 0x00f86621, +/* 0x07e0: i2c_bitr */ + 0xf50137f0, + 0xf106a521, + 0xf403e8e7, + 0x76bb6621, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb606eb21, + 0x11f40464, + 0xd621f51b, + 0x0037f006, + 0x068921f5, + 0x1388e7f1, + 0xf06621f4, + 0x31f4013c, +/* 0x0825: i2c_bitr_done */ +/* 0x0827: i2c_get_byte */ + 0xf000f801, + 0x47f00057, +/* 0x082d: i2c_get_byte_next */ + 0x0154b608, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xe021f550, + 0x0464b607, + 0xfd2b11f4, + 0x42b60553, + 0xd81bf401, + 0xbb0137f0, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x079f21f5, +/* 0x0877: i2c_get_byte_done */ + 0xf80464b6, +/* 0x0879: i2c_put_byte */ + 0x0847f000, +/* 0x087c: i2c_put_byte_next */ + 0xff0142b6, + 0x76bb3854, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6079f21, + 0x11f40464, + 0x0046b034, + 0xbbd81bf4, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x07e021f5, + 0xf40464b6, + 0x76bb0f11, + 0x0136b000, + 0xf4061bf4, +/* 0x08d2: i2c_put_byte_done */ + 0x00f80132, +/* 0x08d4: i2c_addr */ + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x1021f550, + 0x0464b607, + 0xe72911f4, + 0xb6012ec3, + 0x53fd0134, + 0x0076bb05, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60879, +/* 0x0919: i2c_addr_done */ +/* 0x091b: i2c_acquire_addr */ + 0xc700f804, + 0xe4b6f8ce, + 0x14e0b705, +/* 0x0927: i2c_acquire */ + 0xf500f8d0, + 0xf4091b21, + 0xd9f00421, + 0x3421f403, +/* 0x0936: i2c_release */ + 0x21f500f8, + 0x21f4091b, + 0x03daf004, + 0xf83421f4, +/* 0x0945: i2c_recv */ + 0x0132f400, + 0xb6f8c1c7, + 0x16b00214, + 0x3a1ff528, + 0xf413a001, + 0x0032980c, + 0x0ccc13a0, + 0xf4003198, + 0xd0f90231, + 0xd0f9e0f9, + 0x000067f1, + 0x100063f1, + 0xbb016792, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x092721f5, + 0xfc0464b6, + 0x00d6b0d0, + 0x00b31bf5, + 0xbb0057f0, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x08d421f5, + 0xf50464b6, + 0xc700d011, + 0x76bbe0c5, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6087921, + 0x11f50464, + 0x57f000ad, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b608d4, + 0x8a11f504, + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60827, + 0x6a11f404, + 0xbbe05bcb, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x076c21f5, + 0xb90464b6, + 0x74bd025b, +/* 0x0a4b: i2c_recv_not_rd08 */ + 0xb0430ef4, + 0x1bf401d6, + 0x0057f03d, + 0x08d421f5, + 0xc73311f4, + 0x21f5e0c5, + 0x11f40879, + 0x0057f029, + 0x08d421f5, + 0xc71f11f4, + 0x21f5e0b5, + 0x11f40879, + 0x6c21f515, + 0xc774bd07, + 0x1bf408c5, + 0x0232f409, +/* 0x0a8b: i2c_recv_not_wr08 */ +/* 0x0a8b: i2c_recv_done */ + 0xc7030ef4, + 0x21f5f8ce, + 0xe0fc0936, + 0x12f4d0fc, + 0x027cb90a, + 0x02e521f5, +/* 0x0aa0: i2c_recv_exit */ +/* 0x0aa2: i2c_init */ + 0x00f800f8, +/* 0x0aa4: test_recv */ + 0x05d817f1, + 0xb60011cf, + 0x07f10110, + 0x01d005d8, + 0xf104bd00, + 0xf1d900e7, + 0xf5134fe3, + 0xf8021721, +/* 0x0ac5: test_init */ + 0x00e7f100, + 0x1721f508, +/* 0x0acf: idle_recv */ + 0xf800f802, +/* 0x0ad1: idle */ + 0x0031f400, + 0x05d417f1, + 0xb60011cf, + 0x07f10110, + 0x01d005d4, +/* 0x0ae7: idle_loop */ + 0xf004bd00, + 0x32f45817, +/* 0x0aed: idle_proc */ +/* 0x0aed: idle_proc_exec */ + 0xb910f902, + 0x21f5021e, + 0x10fc02ee, + 0xf40911f4, + 0x0ef40231, +/* 0x0b01: idle_proc_next */ + 0x5810b6ef, + 0xf4061fb8, + 0x02f4e61b, + 0x0028f4dd, + 0x00c10ef4, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 new file mode 100644 index 000000000..093dc8188 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 @@ -0,0 +1,70 @@ +/* + * 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 + */ + +#define NVKM_PPWR_CHIPSET GK208 +#define HW_TICKS_PER_US 324 + +#define NVKM_FALCON_PC24 +#define NVKM_FALCON_UNSHIFTED_IO +//#define NVKM_FALCON_MMIO_UAS +//#define NVKM_FALCON_MMIO_TRAP + +#include "macros.fuc" + +.section #gk208_pmu_data +#define INCLUDE_PROC +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_PROC + +#define INCLUDE_DATA +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_DATA +.align 256 + +.section #gk208_pmu_code +#define INCLUDE_CODE +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_CODE +.align 256 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h new file mode 100644 index 000000000..275ec71bc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h @@ -0,0 +1,1730 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gk208_pmu_data[] = { +/* 0x0000: proc_kern */ + 0x52544e49, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: proc_list_head */ + 0x54534f48, + 0x0000042c, + 0x000003df, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x000005ee, + 0x000005e0, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x000005f2, + 0x000005f0, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x000009f3, + 0x0000089d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000a11, + 0x000009f5, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000a1c, + 0x00000a1a, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0268: proc_list_tail */ +/* 0x0268: time_prev */ + 0x00000000, +/* 0x026c: time_next */ + 0x00000000, +/* 0x0270: fifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x02f0: rfifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0370: memx_func_head */ + 0x00000001, + 0x00000000, + 0x0000045c, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x000004cc, + 0x00000003, + 0x00000002, + 0x00000541, + 0x00040004, + 0x00000000, + 0x0000055e, + 0x00010005, + 0x00000000, + 0x00000578, + 0x00010006, + 0x00000000, + 0x0000053c, + 0x00000007, + 0x00000000, + 0x00000584, +/* 0x03c4: memx_func_tail */ +/* 0x03c4: memx_ts_start */ + 0x00000000, +/* 0x03c8: memx_ts_end */ + 0x00000000, +/* 0x03cc: memx_data_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0bcc: memx_data_tail */ +/* 0x0bcc: memx_train_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0ccc: memx_train_tail */ +/* 0x0ccc: i2c_scl_map */ + 0x00000400, + 0x00000800, + 0x00001000, + 0x00002000, + 0x00004000, + 0x00008000, + 0x00010000, + 0x00020000, + 0x00040000, + 0x00080000, +/* 0x0cf4: i2c_sda_map */ + 0x00100000, + 0x00200000, + 0x00400000, + 0x00800000, + 0x01000000, + 0x02000000, + 0x04000000, + 0x08000000, + 0x10000000, + 0x20000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gk208_pmu_code[] = { + 0x02f90ef5, +/* 0x0004: rd32 */ + 0xf607a040, + 0x04bd000e, + 0x0100018d, + 0xf607ac40, + 0x04bd000d, +/* 0x0018: rd32_wait */ + 0xcf07ac4d, + 0xd4f100dd, + 0x1bf47000, + 0x07a44df6, + 0xf800ddcf, +/* 0x002d: wr32 */ + 0x07a04000, + 0xbd000ef6, + 0x07a44004, + 0xbd000df6, + 0x00f28d04, + 0x07ac4001, + 0xbd000df6, +/* 0x0049: wr32_wait */ + 0x07ac4d04, + 0xf100ddcf, + 0xf47000d4, + 0x00f8f61b, +/* 0x0058: nsec */ + 0x80f990f9, + 0x88cf2c08, +/* 0x0061: nsec_loop */ + 0xcf2c0900, + 0x98bb0099, + 0xf49ea602, + 0x80fcf61e, + 0x00f890fc, +/* 0x0074: wait */ + 0x80f990f9, + 0x88cf2c08, +/* 0x007d: wait_loop */ + 0x7eeeb200, + 0xb2000004, + 0x04adfdda, + 0x0bf4aca6, + 0xcf2c0910, + 0x98bb0099, + 0xf49ba602, +/* 0x009a: wait_done */ + 0x80fce61e, + 0x00f890fc, +/* 0x00a0: intr_watchdog */ + 0xb003e998, + 0x0bf40096, + 0x9a0a9828, + 0xf4029abb, + 0x010d0e1c, + 0x00023e7e, + 0x0ef494bd, +/* 0x00bd: intr_watchdog_next_time */ + 0x9b0a9814, + 0xf400a6b0, + 0x9aa6080b, +/* 0x00cb: intr_watchdog_next_time_set */ + 0xb5061cf4, +/* 0x00ce: intr_watchdog_next_proc */ + 0xe9b59b09, + 0x58e0b603, + 0x0268e6b1, + 0xf8c81bf4, +/* 0x00dd: intr */ + 0xbd00f900, + 0xf980f904, + 0xf9a0f990, + 0xf9c0f9b0, + 0xf9e0f9d0, + 0xfe000ff0, + 0x80f90188, + 0xcf045048, + 0x80b60088, + 0x04504001, + 0xbd0008f6, + 0xcf080804, + 0x89c40088, + 0x1f0bf402, + 0x0e9b00b5, + 0x00a07e58, + 0x9b099800, + 0xf40096b0, + 0x34000d0b, + 0xbd0009f6, + 0x9a09b504, +/* 0x0130: intr_skip_watchdog */ + 0x080089e4, + 0x49340bf4, + 0x99cf0688, + 0x029ac400, + 0x4c200bf4, + 0xcccf04c0, + 0xdec0f900, + 0x54534f48, + 0x9f7e000d, + 0xc0fc0002, + 0xf604c040, + 0x04bd000c, +/* 0x0160: intr_subintr_skip_fifo */ + 0xf6068840, + 0x04bd0009, +/* 0x0168: intr_skip_subintr */ + 0xbd00e049, + 0x0489fd90, + 0x08f60400, + 0xfc04bd00, + 0x0088fe80, + 0xe0fcf0fc, + 0xc0fcd0fc, + 0xa0fcb0fc, + 0x80fc90fc, + 0x32f400fc, +/* 0x0193: ticks_from_ns */ + 0xf901f800, + 0x4db0f9c0, + 0x527e0144, + 0xccec0003, + 0xb4b003e8, + 0x0e0bf400, + 0x03e8eeec, + 0x7e01444d, +/* 0x01b3: ticks_from_ns_quit */ + 0xb2000352, + 0xfcb0fcce, +/* 0x01bb: ticks_from_us */ + 0xf900f8c0, + 0x4db0f9c0, + 0x527e0144, + 0xceb20003, + 0xf400b4b0, + 0xe4bd050b, +/* 0x01d0: ticks_from_us_quit */ + 0xc0fcb0fc, +/* 0x01d6: ticks_to_us */ + 0x444d00f8, + 0xecedff01, +/* 0x01de: timer */ + 0x90f900f8, + 0x32f480f9, + 0x03f89810, + 0xf40086b0, + 0x84bd4a1c, + 0x08f63800, + 0x0804bd00, + 0x0088cf34, + 0xbb9a0998, + 0xe9bb0298, + 0x03feb500, + 0x88cf0808, + 0x0284f000, + 0x081c1bf4, + 0x0088cf34, + 0x0bf4e0a6, + 0xf4e8a608, +/* 0x0222: timer_reset */ + 0x34000d1c, + 0xbd000ef6, + 0x9a0eb504, +/* 0x022c: timer_enable */ + 0x38000108, + 0xbd0008f6, +/* 0x0235: timer_done */ + 0x1031f404, + 0x90fc80fc, +/* 0x023e: send_proc */ + 0x80f900f8, + 0xe89890f9, + 0x04e99805, + 0xa60486f0, + 0x2a0bf489, + 0x940398c4, + 0x80b60488, + 0x008ebb18, + 0xb500fa98, + 0x8db5008a, + 0x028cb501, + 0xb6038bb5, + 0x94f00190, + 0x04e9b507, +/* 0x0277: send_done */ + 0xfc0231f4, + 0xf880fc90, +/* 0x027d: find */ + 0x0880f900, + 0x0131f458, +/* 0x0284: find_loop */ + 0xa6008a98, + 0x100bf4ae, + 0xb15880b6, + 0xf4026886, + 0x32f4f11b, +/* 0x0299: find_done */ + 0xfc8eb201, +/* 0x029f: send */ + 0x7e00f880, + 0xf400027d, + 0x00f89b01, +/* 0x02a8: recv */ + 0x80f990f9, + 0x9805e898, + 0x32f404e9, + 0xf489a601, + 0x89c43c0b, + 0x0180b603, + 0xb50784f0, + 0xea9805e8, + 0xfef0f902, + 0xf0f9018f, + 0x9994efb2, + 0x00e9bb04, + 0x9818e0b6, + 0xec9803eb, + 0x01ed9802, + 0xf900ee98, + 0xfef0fca5, + 0x31f400f8, +/* 0x02f3: recv_done */ + 0xfcf0fc01, + 0xf890fc80, +/* 0x02f9: init */ + 0x01084100, + 0xe70011cf, + 0xb6010911, + 0x14fe0814, + 0x00e04100, + 0x01f61c00, + 0x0104bd00, + 0xf61400ff, + 0x04bd0001, + 0x15f10201, + 0x10000800, + 0xbd0001f6, + 0x00dd4104, + 0xffff14f1, + 0xf40010fe, + 0x01011031, + 0x01f63800, + 0x0f04bd00, +/* 0x0341: init_proc */ + 0x01f19858, + 0xf40016b0, + 0x15f9fa0b, + 0xf458f0b6, +/* 0x0352: mulu32_32_64 */ + 0x10f9f20e, + 0x30f920f9, + 0xe19540f9, + 0x10d29510, + 0xb4bdc4bd, + 0xffc0edff, + 0x34b2301d, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xb230e2ff, + 0xff34f134, + 0x1034b6ff, + 0xbb1045b6, + 0xb4bb00c3, + 0x3012ff01, + 0xfc00b3bb, + 0xfc30fc40, + 0xf810fc20, +/* 0x03a1: host_send */ + 0x04b04100, + 0x420011cf, + 0x22cf04a0, + 0xf412a600, + 0x1ec42e0b, + 0x04ee9407, + 0x0270e0b7, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0x00029f7e, + 0xc40110b6, + 0xb0400f1e, + 0x000ef604, + 0x0ef404bd, +/* 0x03dd: host_send_done */ +/* 0x03df: host_recv */ + 0xd100f8c7, + 0x52544e49, + 0x0bf4e1a6, +/* 0x03e9: host_recv_wait */ + 0x04cc41bb, + 0x420011cf, + 0x22cf04c8, + 0x0816f000, + 0x0bf412a6, + 0x0723c4ef, + 0xb70434b6, + 0xb502f030, + 0x3cb5033b, + 0x013db502, + 0xb6003eb5, + 0x24f00120, + 0x04c8400f, + 0xbd0002f6, + 0x00400204, + 0x0002f600, + 0x00f804bd, +/* 0x042c: host_init */ + 0xb6008041, + 0x15f11014, + 0xd0400270, + 0x0001f604, + 0x804104bd, + 0x1014b600, + 0x02f015f1, + 0xf604dc40, + 0x04bd0001, + 0xc4400101, + 0x0001f604, + 0x00f804bd, +/* 0x045c: memx_func_enter */ + 0x47162046, + 0x6eb2f55d, + 0x0000047e, + 0x87fdd8b2, + 0xf960f904, + 0xfcd0fc80, + 0x002d7ee0, + 0xb2fe0700, + 0x00047e6e, + 0xfdd8b200, + 0x60f90487, + 0xd0fc80f9, + 0x2d7ee0fc, + 0xf0460000, + 0x7e6eb226, + 0xb2000004, + 0x0487fdd8, + 0x80f960f9, + 0xe0fcd0fc, + 0x00002d7e, + 0xe0400406, + 0x0006f607, +/* 0x04b6: memx_func_enter_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0x2c06f70b, + 0xb50066cf, + 0x00f8f106, +/* 0x04cc: memx_func_leave */ + 0x66cf2c06, + 0xf206b500, + 0xe4400406, + 0x0006f607, +/* 0x04de: memx_func_leave_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0xf046f71b, + 0xb2010726, + 0x00047e6e, + 0xfdd8b200, + 0x60f90587, + 0xd0fc80f9, + 0x2d7ee0fc, + 0x20460000, + 0x7e6eb216, + 0xb2000004, + 0x0587fdd8, + 0x80f960f9, + 0xe0fcd0fc, + 0x00002d7e, + 0xb20aa247, + 0x00047e6e, + 0xfdd8b200, + 0x60f90587, + 0xd0fc80f9, + 0x2d7ee0fc, + 0x00f80000, +/* 0x053c: memx_func_wait_vblank */ + 0xf80410b6, +/* 0x0541: memx_func_wr32 */ + 0x00169800, + 0xb6011598, + 0x60f90810, + 0xd0fc50f9, + 0x2d7ee0fc, + 0x42b60000, + 0xe81bf402, +/* 0x055e: memx_func_wait */ + 0x2c0800f8, + 0x980088cf, + 0x1d98001e, + 0x021c9801, + 0xb6031b98, + 0x747e1010, + 0x00f80000, +/* 0x0578: memx_func_delay */ + 0xb6001e98, + 0x587e0410, + 0x00f80000, +/* 0x0584: memx_func_train */ +/* 0x0586: memx_exec */ + 0xe0f900f8, + 0xc1b2d0f9, +/* 0x058e: memx_exec_next */ + 0x1398b2b2, + 0x0410b600, + 0x01f034e7, + 0x01e033e7, + 0xf00132b6, + 0x35980c30, + 0xa655f9de, + 0xe51ef412, + 0x98f10b98, + 0xcbbbf20c, + 0x07c44b02, + 0xfc00bbcf, + 0x7ee0fcd0, + 0xf800029f, +/* 0x05c5: memx_info */ + 0x01c67000, +/* 0x05cb: memx_info_data */ + 0x4c0c0bf4, + 0x004b03cc, + 0x090ef408, +/* 0x05d4: memx_info_train */ + 0x4b0bcc4c, +/* 0x05da: memx_info_send */ + 0x9f7e0100, + 0x00f80002, +/* 0x05e0: memx_recv */ + 0xf401d6b0, + 0xd6b0a30b, + 0xdc0bf400, +/* 0x05ee: memx_init */ + 0x00f800f8, +/* 0x05f0: perf_recv */ +/* 0x05f2: perf_init */ + 0x00f800f8, +/* 0x05f4: i2c_drive_scl */ + 0xf40036b0, + 0xe0400d0b, + 0x0001f607, + 0x00f804bd, +/* 0x0604: i2c_drive_scl_lo */ + 0xf607e440, + 0x04bd0001, +/* 0x060e: i2c_drive_sda */ + 0x36b000f8, + 0x0d0bf400, + 0xf607e040, + 0x04bd0002, +/* 0x061e: i2c_drive_sda_lo */ + 0xe44000f8, + 0x0002f607, + 0x00f804bd, +/* 0x0628: i2c_sense_scl */ + 0x430132f4, + 0x33cf07c4, + 0x0431fd00, + 0xf4060bf4, +/* 0x063a: i2c_sense_scl_done */ + 0x00f80131, +/* 0x063c: i2c_sense_sda */ + 0x430132f4, + 0x33cf07c4, + 0x0432fd00, + 0xf4060bf4, +/* 0x064e: i2c_sense_sda_done */ + 0x00f80131, +/* 0x0650: i2c_raise_scl */ + 0x984440f9, + 0x7e010308, +/* 0x065b: i2c_raise_scl_wait */ + 0x4e0005f4, + 0x587e03e8, + 0x287e0000, + 0x01f40006, + 0x0142b609, +/* 0x066f: i2c_raise_scl_done */ + 0xfcef1bf4, +/* 0x0673: i2c_start */ + 0x7e00f840, + 0xf4000628, + 0x3c7e0d11, + 0x11f40006, + 0x2e0ef406, +/* 0x0684: i2c_start_rep */ + 0xf47e0003, + 0x01030005, + 0x00060e7e, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x06507e50, + 0x0464b600, +/* 0x06af: i2c_start_send */ + 0x031d11f4, + 0x060e7e00, + 0x13884e00, + 0x0000587e, + 0xf47e0003, + 0x884e0005, + 0x00587e13, +/* 0x06c9: i2c_start_out */ +/* 0x06cb: i2c_stop */ + 0x0300f800, + 0x05f47e00, + 0x7e000300, + 0x4e00060e, + 0x587e03e8, + 0x01030000, + 0x0005f47e, + 0x7e13884e, + 0x03000058, + 0x060e7e01, + 0x13884e00, + 0x0000587e, +/* 0x06fa: i2c_bitw */ + 0x0e7e00f8, + 0xe84e0006, + 0x00587e03, + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x507e50fc, + 0x64b60006, + 0x1711f404, + 0x7e13884e, + 0x03000058, + 0x05f47e00, + 0x13884e00, + 0x0000587e, +/* 0x0738: i2c_bitw_out */ +/* 0x073a: i2c_bitr */ + 0x010300f8, + 0x00060e7e, + 0x7e03e84e, + 0xbb000058, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0006507e, + 0xf40464b6, + 0x3c7e1a11, + 0x00030006, + 0x0005f47e, + 0x7e13884e, + 0xf0000058, + 0x31f4013c, +/* 0x077d: i2c_bitr_done */ +/* 0x077f: i2c_get_byte */ + 0x0500f801, +/* 0x0783: i2c_get_byte_next */ + 0xb6080400, + 0x76bb0154, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb600073a, + 0x11f40464, + 0x0553fd2a, + 0xf40142b6, + 0x0103d81b, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x06fa7e50, + 0x0464b600, +/* 0x07cc: i2c_get_byte_done */ +/* 0x07ce: i2c_put_byte */ + 0x080400f8, +/* 0x07d0: i2c_put_byte_next */ + 0xff0142b6, + 0x76bb3854, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb60006fa, + 0x11f40464, + 0x0046b034, + 0xbbd81bf4, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x00073a7e, + 0xf40464b6, + 0x76bb0f11, + 0x0136b000, + 0xf4061bf4, +/* 0x0826: i2c_put_byte_done */ + 0x00f80132, +/* 0x0828: i2c_addr */ + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x06737e50, + 0x0464b600, + 0xe72911f4, + 0xb6012ec3, + 0x53fd0134, + 0x0076bb05, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0xce7e50fc, + 0x64b60007, +/* 0x086d: i2c_addr_done */ +/* 0x086f: i2c_acquire_addr */ + 0xc700f804, + 0xe4b6f8ce, + 0x14e0b705, +/* 0x087b: i2c_acquire */ + 0x7e00f8d0, + 0x7e00086f, + 0xf0000004, + 0x2d7e03d9, + 0x00f80000, +/* 0x088c: i2c_release */ + 0x00086f7e, + 0x0000047e, + 0x7e03daf0, + 0xf800002d, +/* 0x089d: i2c_recv */ + 0x0132f400, + 0xb6f8c1c7, + 0x16b00214, + 0x341ff528, + 0xf413b801, + 0x3298000c, + 0xcc13b800, + 0x3198000c, + 0x0231f400, + 0xe0f9d0f9, + 0x00d6d0f9, + 0x92100000, + 0x76bb0167, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb600087b, + 0xd0fc0464, + 0xf500d6b0, + 0x0500b01b, + 0x0076bb00, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x287e50fc, + 0x64b60008, + 0xcc11f504, + 0xe0c5c700, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x07ce7e50, + 0x0464b600, + 0x00a911f5, + 0x76bb0105, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb6000828, + 0x11f50464, + 0x76bb0087, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb600077f, + 0x11f40464, + 0xe05bcb67, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x06cb7e50, + 0x0464b600, + 0x74bd5bb2, +/* 0x099f: i2c_recv_not_rd08 */ + 0xb0410ef4, + 0x1bf401d6, + 0x7e00053b, + 0xf4000828, + 0xc5c73211, + 0x07ce7ee0, + 0x2811f400, + 0x287e0005, + 0x11f40008, + 0xe0b5c71f, + 0x0007ce7e, + 0x7e1511f4, + 0xbd0006cb, + 0x08c5c774, + 0xf4091bf4, + 0x0ef40232, +/* 0x09dd: i2c_recv_not_wr08 */ +/* 0x09dd: i2c_recv_done */ + 0xf8cec703, + 0x00088c7e, + 0xd0fce0fc, + 0xb20912f4, + 0x029f7e7c, +/* 0x09f1: i2c_recv_exit */ +/* 0x09f3: i2c_init */ + 0xf800f800, +/* 0x09f5: test_recv */ + 0x04584100, + 0xb60011cf, + 0x58400110, + 0x0001f604, + 0x00de04bd, + 0x7e134fd9, + 0xf80001de, +/* 0x0a11: test_init */ + 0x08004e00, + 0x0001de7e, +/* 0x0a1a: idle_recv */ + 0x00f800f8, +/* 0x0a1c: idle */ + 0x410031f4, + 0x11cf0454, + 0x0110b600, + 0xf6045440, + 0x04bd0001, +/* 0x0a30: idle_loop */ + 0x32f45801, +/* 0x0a35: idle_proc */ +/* 0x0a35: idle_proc_exec */ + 0xb210f902, + 0x02a87e1e, + 0xf410fc00, + 0x31f40911, + 0xf00ef402, +/* 0x0a48: idle_proc_next */ + 0xa65810b6, + 0xe81bf41f, + 0xf4e002f4, + 0x0ef40028, + 0x000000c6, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 new file mode 100644 index 000000000..393049fc8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 @@ -0,0 +1,70 @@ +/* + * 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 + */ + +#define NVKM_PPWR_CHIPSET GT215 +#define HW_TICKS_PER_US 203 // should be 202.5 + +//#define NVKM_FALCON_PC24 +//#define NVKM_FALCON_UNSHIFTED_IO +//#define NVKM_FALCON_MMIO_UAS +//#define NVKM_FALCON_MMIO_TRAP + +#include "macros.fuc" + +.section #gt215_pmu_data +#define INCLUDE_PROC +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_PROC + +#define INCLUDE_DATA +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_DATA +.align 256 + +.section #gt215_pmu_code +#define INCLUDE_CODE +#include "kernel.fuc" +#include "arith.fuc" +#include "host.fuc" +#include "memx.fuc" +#include "perf.fuc" +#include "i2c_.fuc" +#include "test.fuc" +#include "idle.fuc" +#undef INCLUDE_CODE +.align 256 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h new file mode 100644 index 000000000..4b071e9be --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h @@ -0,0 +1,1867 @@ +/* SPDX-License-Identifier: MIT */ +static uint32_t gt215_pmu_data[] = { +/* 0x0000: proc_kern */ + 0x52544e49, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: proc_list_head */ + 0x54534f48, + 0x0000050a, + 0x000004a7, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x584d454d, + 0x00000833, + 0x00000825, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x46524550, + 0x00000837, + 0x00000835, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5f433249, + 0x00000c67, + 0x00000b0a, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x54534554, + 0x00000c90, + 0x00000c69, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x454c4449, + 0x00000c9c, + 0x00000c9a, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0268: proc_list_tail */ +/* 0x0268: time_prev */ + 0x00000000, +/* 0x026c: time_next */ + 0x00000000, +/* 0x0270: fifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x02f0: rfifo_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0370: memx_func_head */ + 0x00000001, + 0x00000000, + 0x00000549, +/* 0x037c: memx_func_next */ + 0x00000002, + 0x00000000, + 0x0000059f, + 0x00000003, + 0x00000002, + 0x0000062f, + 0x00040004, + 0x00000000, + 0x0000064b, + 0x00010005, + 0x00000000, + 0x00000668, + 0x00010006, + 0x00000000, + 0x000005ef, + 0x00000007, + 0x00000000, + 0x00000673, +/* 0x03c4: memx_func_tail */ +/* 0x03c4: memx_ts_start */ + 0x00000000, +/* 0x03c8: memx_ts_end */ + 0x00000000, +/* 0x03cc: memx_data_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0bcc: memx_data_tail */ +/* 0x0bcc: memx_train_head */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0ccc: memx_train_tail */ +/* 0x0ccc: i2c_scl_map */ + 0x00001000, + 0x00004000, + 0x00010000, + 0x00000100, + 0x00040000, + 0x00100000, + 0x00400000, + 0x01000000, + 0x04000000, + 0x10000000, +/* 0x0cf4: i2c_sda_map */ + 0x00002000, + 0x00008000, + 0x00020000, + 0x00000200, + 0x00080000, + 0x00200000, + 0x00800000, + 0x02000000, + 0x08000000, + 0x20000000, +/* 0x0d1c: i2c_ctrl */ + 0x0000e138, + 0x0000e150, + 0x0000e168, + 0x0000e180, + 0x0000e254, + 0x0000e274, + 0x0000e764, + 0x0000e780, + 0x0000e79c, + 0x0000e7b8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static uint32_t gt215_pmu_code[] = { + 0x03920ef5, +/* 0x0004: rd32 */ + 0x07a007f1, + 0xd00604b6, + 0x04bd000e, + 0x0001d7f1, + 0xf101d3f0, + 0xb607ac07, + 0x0dd00604, +/* 0x0023: rd32_wait */ + 0xf104bd00, + 0xb607acd7, + 0xddcf06d4, + 0x00d4f100, + 0xf21bf470, + 0x07a4d7f1, + 0xcf06d4b6, + 0x00f800dd, +/* 0x0040: wr32 */ + 0x07a007f1, + 0xd00604b6, + 0x04bd000e, + 0x07a407f1, + 0xd00604b6, + 0x04bd000d, + 0x00f2d7f1, + 0xf101d3f0, + 0xb607ac07, + 0x0dd00604, +/* 0x006b: wr32_wait */ + 0xf104bd00, + 0xb607acd7, + 0xddcf06d4, + 0x00d4f100, + 0xf21bf470, +/* 0x007e: nsec */ + 0x90f900f8, + 0x87f080f9, + 0x0684b62c, +/* 0x008b: nsec_loop */ + 0xf00088cf, + 0x94b62c97, + 0x0099cf06, + 0xb80298bb, + 0x1ef4069e, + 0xfc80fcf1, +/* 0x00a3: wait */ + 0xf900f890, + 0xf080f990, + 0x84b62c87, + 0x0088cf06, +/* 0x00b0: wait_loop */ + 0xf402eeb9, + 0xdab90421, + 0x04adfd02, + 0xf406acb8, + 0x97f0150b, + 0x0694b62c, + 0xbb0099cf, + 0x9bb80298, + 0xdf1ef406, +/* 0x00d4: wait_done */ + 0x90fc80fc, +/* 0x00da: intr_watchdog */ + 0xe99800f8, + 0x0096b003, + 0x982a0bf4, + 0x9abb9a0a, + 0x0f1cf402, + 0xf501d7f0, + 0xbd02d121, + 0x150ef494, +/* 0x00f8: intr_watchdog_next_time */ + 0xb09b0a98, + 0x0bf400a6, + 0x069ab809, +/* 0x0107: intr_watchdog_next_time_set */ + 0x80061cf4, +/* 0x010a: intr_watchdog_next_proc */ + 0xe9809b09, + 0x58e0b603, + 0x0268e6b1, + 0xf8c61bf4, +/* 0x0119: intr */ + 0xbd00f900, + 0xf980f904, + 0xf9a0f990, + 0xf9c0f9b0, + 0xf9e0f9d0, + 0x00f7f0f0, + 0xf90188fe, + 0xd087f180, + 0x0684b605, + 0xb60088cf, + 0x07f10180, + 0x04b605d0, + 0x0008d006, + 0x87f004bd, + 0x0684b608, + 0xc40088cf, + 0x0bf40289, + 0x9b008023, + 0xf458e7f0, + 0x0998da21, + 0x0096b09b, + 0xf0110bf4, + 0x04b63407, + 0x0009d006, + 0x098004bd, +/* 0x017d: intr_skip_watchdog */ + 0x0089e49a, + 0x480bf408, + 0x068897f1, + 0xcf0694b6, + 0x9ac40099, + 0x2c0bf402, + 0x04c0c7f1, + 0xcf06c4b6, + 0xc0f900cc, + 0x4f48e7f1, + 0x5453e3f1, + 0xf500d7f0, + 0xfc033621, + 0xc007f1c0, + 0x0604b604, + 0xbd000cd0, +/* 0x01bd: intr_subintr_skip_fifo */ + 0x8807f104, + 0x0604b606, + 0xbd0009d0, +/* 0x01c9: intr_skip_subintr */ + 0xe097f104, + 0xfd90bd00, + 0x07f00489, + 0x0604b604, + 0xbd0008d0, + 0xfe80fc04, + 0xf0fc0088, + 0xd0fce0fc, + 0xb0fcc0fc, + 0x90fca0fc, + 0x00fc80fc, + 0xf80032f4, +/* 0x01f9: ticks_from_ns */ + 0xf9c0f901, + 0xcbd7f1b0, + 0x00d3f000, + 0x040b21f5, + 0x03e8ccec, + 0xf400b4b0, + 0xeeec120b, + 0xd7f103e8, + 0xd3f000cb, + 0x0b21f500, +/* 0x0221: ticks_from_ns_quit */ + 0x02ceb904, + 0xc0fcb0fc, +/* 0x022a: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f000cb, + 0x0b21f500, + 0x02ceb904, + 0xf400b4b0, + 0xe4bd050b, +/* 0x0244: ticks_from_us_quit */ + 0xc0fcb0fc, +/* 0x024a: ticks_to_us */ + 0xd7f100f8, + 0xd3f000cb, + 0xecedff00, +/* 0x0256: timer */ + 0x90f900f8, + 0x32f480f9, + 0x03f89810, + 0xf40086b0, + 0x84bd651c, + 0xb63807f0, + 0x08d00604, + 0xf004bd00, + 0x84b63487, + 0x0088cf06, + 0xbb9a0998, + 0xe9bb0298, + 0x03fe8000, + 0xb60887f0, + 0x88cf0684, + 0x0284f000, + 0xf0261bf4, + 0x84b63487, + 0x0088cf06, + 0xf406e0b8, + 0xe8b8090b, + 0x111cf406, +/* 0x02ac: timer_reset */ + 0xb63407f0, + 0x0ed00604, + 0x8004bd00, +/* 0x02ba: timer_enable */ + 0x87f09a0e, + 0x3807f001, + 0xd00604b6, + 0x04bd0008, +/* 0x02c8: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x02d1: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89b80486, + 0x2a0bf406, + 0x940398c4, + 0x80b60488, + 0x008ebb18, + 0x8000fa98, + 0x8d80008a, + 0x028c8001, + 0xb6038b80, + 0x94f00190, + 0x04e98007, +/* 0x030b: send_done */ + 0xfc0231f4, + 0xf880fc90, +/* 0x0311: find */ + 0xf080f900, + 0x31f45887, +/* 0x0319: find_loop */ + 0x008a9801, + 0xf406aeb8, + 0x80b6100b, + 0x6886b158, + 0xf01bf402, +/* 0x032f: find_done */ + 0xb90132f4, + 0x80fc028e, +/* 0x0336: send */ + 0x21f500f8, + 0x01f40311, +/* 0x033f: recv */ + 0xf900f897, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0xf40689b8, + 0x89c43d0b, + 0x0180b603, + 0x800784f0, + 0xea9805e8, + 0xfef0f902, + 0xf0f9018f, + 0x9402efb9, + 0xe9bb0499, + 0x18e0b600, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0xf0fca5f9, + 0xf400f8fe, + 0xf0fc0131, +/* 0x038c: recv_done */ + 0x90fc80fc, +/* 0x0392: init */ + 0x17f100f8, + 0x14b60108, + 0x0011cf06, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xd00604b6, + 0x04bd0001, + 0xf0ff17f0, + 0x04b61407, + 0x0001d006, + 0x17f004bd, + 0x0015f102, + 0x1007f008, + 0xd00604b6, + 0x04bd0001, + 0x011917f1, + 0xf10013f0, + 0xfeffff14, + 0x31f40010, + 0x0117f010, + 0xb63807f0, + 0x01d00604, + 0xf004bd00, +/* 0x03fa: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x040b: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x045c: host_send */ + 0x04b017f1, + 0xcf0614b6, + 0x27f10011, + 0x24b604a0, + 0x0022cf06, + 0xf40612b8, + 0x1ec4320b, + 0x04ee9407, + 0x0270e0b7, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0x033621f5, + 0xc40110b6, + 0x07f10f1e, + 0x04b604b0, + 0x000ed006, + 0x0ef404bd, +/* 0x04a5: host_send_done */ +/* 0x04a7: host_recv */ + 0xf100f8ba, + 0xf14e4917, + 0xb8525413, + 0x0bf406e1, +/* 0x04b5: host_recv_wait */ + 0xcc17f1aa, + 0x0614b604, + 0xf10011cf, + 0xb604c827, + 0x22cf0624, + 0x0816f000, + 0xf40612b8, + 0x23c4e60b, + 0x0434b607, + 0x02f030b7, + 0x80033b80, + 0x3d80023c, + 0x003e8001, + 0xf00120b6, + 0x07f10f24, + 0x04b604c8, + 0x0002d006, + 0x27f004bd, + 0x0007f040, + 0xd00604b6, + 0x04bd0002, +/* 0x050a: host_init */ + 0x17f100f8, + 0x14b60080, + 0x7015f110, + 0xd007f102, + 0x0604b604, + 0xbd0001d0, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xd00604b6, + 0x04bd0001, + 0xf10117f0, + 0xb604c407, + 0x01d00604, + 0xf804bd00, +/* 0x0549: memx_func_enter */ + 0x1087f100, + 0x028eb916, + 0xb90421f4, + 0x67f102d7, + 0x63f1fffc, + 0x76fdffff, + 0x0267f004, + 0xf90576fd, + 0xfc70f980, + 0xf4e0fcd0, + 0x67f04021, + 0xe007f104, + 0x0604b607, + 0xbd0006d0, +/* 0x0581: memx_func_enter_wait */ + 0xc067f104, + 0x0664b607, + 0xf00066cf, + 0x0bf40464, + 0x2c67f0f3, + 0xcf0664b6, + 0x06800066, +/* 0x059f: memx_func_leave */ + 0xf000f8f1, + 0x64b62c67, + 0x0066cf06, + 0xf0f20680, + 0x07f10467, + 0x04b607e4, + 0x0006d006, +/* 0x05ba: memx_func_leave_wait */ + 0x67f104bd, + 0x64b607c0, + 0x0066cf06, + 0xf40464f0, + 0x87f1f31b, + 0x8eb91610, + 0x0421f402, + 0xf102d7b9, + 0xf1ffcc67, + 0xfdffff63, + 0x80f90476, + 0xd0fc70f9, + 0x21f4e0fc, +/* 0x05ef: memx_func_wait_vblank */ + 0x9800f840, + 0x66b00016, + 0x120bf400, + 0xf40166b0, + 0x0ef4060b, +/* 0x0601: memx_func_wait_vblank_head1 */ + 0x2077f02c, +/* 0x0607: memx_func_wait_vblank_head0 */ + 0xf0060ef4, +/* 0x060a: memx_func_wait_vblank_0 */ + 0x67f10877, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x061a: memx_func_wait_vblank_1 */ + 0x67f1f31b, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x062a: memx_func_wait_vblank_fini */ + 0x10b6f30b, +/* 0x062f: memx_func_wr32 */ + 0x9800f804, + 0x15980016, + 0x0810b601, + 0x50f960f9, + 0xe0fcd0fc, + 0xb64021f4, + 0x1bf40242, +/* 0x064b: memx_func_wait */ + 0xf000f8e9, + 0x84b62c87, + 0x0088cf06, + 0x98001e98, + 0x1c98011d, + 0x031b9802, + 0xf41010b6, + 0x00f8a321, +/* 0x0668: memx_func_delay */ + 0xb6001e98, + 0x21f40410, +/* 0x0673: memx_func_train */ + 0xf000f87e, + 0x77f00357, + 0x0097f100, + 0x7093f000, + 0xf4029eb9, + 0xd8b90421, + 0x10e7f102, + 0x7e21f427, +/* 0x0690: memx_func_train_loop_outer */ + 0x010158e0, + 0x020083f1, + 0x11e097f1, + 0xf91193f0, + 0xfc80f990, + 0xf4e0fcd0, + 0x50f94021, +/* 0x06af: memx_func_train_loop_inner */ + 0xf10067f0, + 0xff111187, + 0x98949068, + 0x0589fd10, + 0x072097f1, + 0xf91093f0, + 0xfc80f990, + 0xf4e0fcd0, + 0x97f14021, + 0x93f00080, + 0x029eb910, + 0xb90421f4, + 0x88c502d8, + 0xf990f920, + 0xfcd0fc80, + 0x4021f4e0, + 0x053c97f1, + 0xf11093f0, + 0xf1300287, + 0xf9800083, + 0xfc80f990, + 0xf4e0fcd0, + 0xe7f14021, + 0xe3f00560, + 0x00d7f110, + 0x00d3f100, + 0x00dc9080, + 0x8480b7f1, + 0xf41eb3f0, + 0x57f0a321, + 0xff97f100, + 0x0093f1ff, +/* 0x072d: memx_func_train_loop_4x */ + 0x80a7f183, + 0x10a3f000, + 0xf402aeb9, + 0xd8b90421, + 0xdfb7f102, + 0xffb3f1ff, + 0x048bfdff, + 0x80f9a0f9, + 0xe0fcd0fc, + 0xf14021f4, + 0xf0053ca7, + 0x87f110a3, + 0x83f13002, + 0xa0f98000, + 0xd0fc80f9, + 0x21f4e0fc, + 0x60e7f140, + 0x10e3f005, + 0x0000d7f1, + 0x8000d3f1, + 0xf102dcb9, + 0xf02710b7, + 0x21f400b3, + 0x02eeb9a3, + 0xb90421f4, + 0x9dff02dd, + 0x0150b694, + 0xf4045670, + 0x7aa0921e, + 0xa9800bcc, + 0x0160b600, + 0x700470b6, + 0x1ef51066, + 0x50fcff01, + 0x700150b6, + 0x1ef50756, + 0x00f8fed6, +/* 0x07c0: memx_exec */ + 0xd0f9e0f9, + 0xb902c1b9, +/* 0x07ca: memx_exec_next */ + 0x139802b2, + 0x0410b600, + 0x01f034e7, + 0x01e033e7, + 0xf00132b6, + 0x35980c30, + 0xb855f9de, + 0x1ef40612, + 0xf10b98e4, + 0xbbf20c98, + 0xb7f102cb, + 0xb4b607c4, + 0x00bbcf06, + 0xe0fcd0fc, + 0x033621f5, +/* 0x0806: memx_info */ + 0xc67000f8, + 0x0e0bf401, +/* 0x080c: memx_info_data */ + 0x03ccc7f1, + 0x0800b7f1, +/* 0x0817: memx_info_train */ + 0xf10b0ef4, + 0xf10bccc7, +/* 0x081f: memx_info_send */ + 0xf50100b7, + 0xf8033621, +/* 0x0825: memx_recv */ + 0x01d6b000, + 0xb0980bf4, + 0x0bf400d6, +/* 0x0833: memx_init */ + 0xf800f8d8, +/* 0x0835: perf_recv */ +/* 0x0837: perf_init */ + 0xf800f800, +/* 0x0839: i2c_drive_scl */ + 0x0036b000, + 0xf1110bf4, + 0xb607e007, + 0x01d00604, + 0xf804bd00, +/* 0x084d: i2c_drive_scl_lo */ + 0xe407f100, + 0x0604b607, + 0xbd0001d0, +/* 0x085b: i2c_drive_sda */ + 0xb000f804, + 0x0bf40036, + 0xe007f111, + 0x0604b607, + 0xbd0002d0, +/* 0x086f: i2c_drive_sda_lo */ + 0xf100f804, + 0xb607e407, + 0x02d00604, + 0xf804bd00, +/* 0x087d: i2c_sense_scl */ + 0x0132f400, + 0x07c437f1, + 0xcf0634b6, + 0x31fd0033, + 0x060bf404, +/* 0x0893: i2c_sense_scl_done */ + 0xf80131f4, +/* 0x0895: i2c_sense_sda */ + 0x0132f400, + 0x07c437f1, + 0xcf0634b6, + 0x32fd0033, + 0x060bf404, +/* 0x08ab: i2c_sense_sda_done */ + 0xf80131f4, +/* 0x08ad: i2c_raise_scl */ + 0xf140f900, + 0xf0089847, + 0x21f50137, +/* 0x08ba: i2c_raise_scl_wait */ + 0xe7f10839, + 0x21f403e8, + 0x7d21f57e, + 0x0901f408, + 0xf40142b6, +/* 0x08ce: i2c_raise_scl_done */ + 0x40fcef1b, +/* 0x08d2: i2c_start */ + 0x21f500f8, + 0x11f4087d, + 0x9521f50d, + 0x0611f408, +/* 0x08e3: i2c_start_rep */ + 0xf0300ef4, + 0x21f50037, + 0x37f00839, + 0x5b21f501, + 0x0076bb08, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b608ad, + 0x1f11f404, +/* 0x0910: i2c_start_send */ + 0xf50037f0, + 0xf1085b21, + 0xf41388e7, + 0x37f07e21, + 0x3921f500, + 0x88e7f108, + 0x7e21f413, +/* 0x092c: i2c_start_out */ +/* 0x092e: i2c_stop */ + 0x37f000f8, + 0x3921f500, + 0x0037f008, + 0x085b21f5, + 0x03e8e7f1, + 0xf07e21f4, + 0x21f50137, + 0xe7f10839, + 0x21f41388, + 0x0137f07e, + 0x085b21f5, + 0x1388e7f1, + 0xf87e21f4, +/* 0x0961: i2c_bitw */ + 0x5b21f500, + 0xe8e7f108, + 0x7e21f403, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xad21f550, + 0x0464b608, + 0xf11811f4, + 0xf41388e7, + 0x37f07e21, + 0x3921f500, + 0x88e7f108, + 0x7e21f413, +/* 0x09a0: i2c_bitw_out */ +/* 0x09a2: i2c_bitr */ + 0x37f000f8, + 0x5b21f501, + 0xe8e7f108, + 0x7e21f403, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xad21f550, + 0x0464b608, + 0xf51b11f4, + 0xf0089521, + 0x21f50037, + 0xe7f10839, + 0x21f41388, + 0x013cf07e, +/* 0x09e7: i2c_bitr_done */ + 0xf80131f4, +/* 0x09e9: i2c_get_byte */ + 0x0057f000, +/* 0x09ef: i2c_get_byte_next */ + 0xb60847f0, + 0x76bb0154, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb609a221, + 0x11f40464, + 0x0553fd2b, + 0xf40142b6, + 0x37f0d81b, + 0x0076bb01, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b60961, +/* 0x0a39: i2c_get_byte_done */ +/* 0x0a3b: i2c_put_byte */ + 0xf000f804, +/* 0x0a3e: i2c_put_byte_next */ + 0x42b60847, + 0x3854ff01, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x6121f550, + 0x0464b609, + 0xb03411f4, + 0x1bf40046, + 0x0076bbd8, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b609a2, + 0x0f11f404, + 0xb00076bb, + 0x1bf40136, + 0x0132f406, +/* 0x0a94: i2c_put_byte_done */ +/* 0x0a96: i2c_addr */ + 0x76bb00f8, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb608d221, + 0x11f40464, + 0x2ec3e729, + 0x0134b601, + 0xbb0553fd, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0a3b21f5, +/* 0x0adb: i2c_addr_done */ + 0xf80464b6, +/* 0x0add: i2c_acquire_addr */ + 0xf8cec700, + 0xb702e4b6, + 0x980d1ce0, + 0x00f800ee, +/* 0x0aec: i2c_acquire */ + 0x0add21f5, + 0xf00421f4, + 0x21f403d9, +/* 0x0afb: i2c_release */ + 0xf500f840, + 0xf40add21, + 0xdaf00421, + 0x4021f403, +/* 0x0b0a: i2c_recv */ + 0x32f400f8, + 0xf8c1c701, + 0xb00214b6, + 0x1ff52816, + 0x13a0013a, + 0x32980cf4, + 0xcc13a000, + 0x0031980c, + 0xf90231f4, + 0xf9e0f9d0, + 0x0067f1d0, + 0x0063f100, + 0x01679210, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xec21f550, + 0x0464b60a, + 0xd6b0d0fc, + 0xb31bf500, + 0x0057f000, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x9621f550, + 0x0464b60a, + 0x00d011f5, + 0xbbe0c5c7, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0a3b21f5, + 0xf50464b6, + 0xf000ad11, + 0x76bb0157, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb60a9621, + 0x11f50464, + 0x76bb008a, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb609e921, + 0x11f40464, + 0xe05bcb6a, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x2e21f550, + 0x0464b609, + 0xbd025bb9, + 0x430ef474, +/* 0x0c10: i2c_recv_not_rd08 */ + 0xf401d6b0, + 0x57f03d1b, + 0x9621f500, + 0x3311f40a, + 0xf5e0c5c7, + 0xf40a3b21, + 0x57f02911, + 0x9621f500, + 0x1f11f40a, + 0xf5e0b5c7, + 0xf40a3b21, + 0x21f51511, + 0x74bd092e, + 0xf408c5c7, + 0x32f4091b, + 0x030ef402, +/* 0x0c50: i2c_recv_not_wr08 */ +/* 0x0c50: i2c_recv_done */ + 0xf5f8cec7, + 0xfc0afb21, + 0xf4d0fce0, + 0x7cb90a12, + 0x3621f502, +/* 0x0c65: i2c_recv_exit */ +/* 0x0c67: i2c_init */ + 0xf800f803, +/* 0x0c69: test_recv */ + 0xd817f100, + 0x0614b605, + 0xb60011cf, + 0x07f10110, + 0x04b605d8, + 0x0001d006, + 0xe7f104bd, + 0xe3f1d900, + 0x21f5134f, + 0x00f80256, +/* 0x0c90: test_init */ + 0x0800e7f1, + 0x025621f5, +/* 0x0c9a: idle_recv */ + 0x00f800f8, +/* 0x0c9c: idle */ + 0xf10031f4, + 0xb605d417, + 0x11cf0614, + 0x0110b600, + 0x05d407f1, + 0xd00604b6, + 0x04bd0001, +/* 0x0cb8: idle_loop */ + 0xf45817f0, +/* 0x0cbe: idle_proc */ +/* 0x0cbe: idle_proc_exec */ + 0x10f90232, + 0xf5021eb9, + 0xfc033f21, + 0x0911f410, + 0xf40231f4, +/* 0x0cd2: idle_proc_next */ + 0x10b6ef0e, + 0x061fb858, + 0xf4e61bf4, + 0x28f4dd02, + 0xbb0ef400, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc new file mode 100644 index 000000000..f2420a37f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc @@ -0,0 +1,150 @@ +/* + * 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 + */ + +#ifdef INCLUDE_PROC +process(PROC_HOST, #host_init, #host_recv) +#endif + +/****************************************************************************** + * HOST data segment + *****************************************************************************/ +#ifdef INCLUDE_DATA +// HOST (R)FIFO packet format +.equ #fifo_process 0x00 +.equ #fifo_message 0x04 +.equ #fifo_data0 0x08 +.equ #fifo_data1 0x0c + +// HOST HOST->PWR queue description +.equ #fifo_qlen 4 // log2(size of queue entry in bytes) +.equ #fifo_qnum 3 // log2(max number of entries in queue) +.equ #fifo_qmaskb (1 << #fifo_qnum) // max number of entries in queue +.equ #fifo_qmaskp (#fifo_qmaskb - 1) +.equ #fifo_qmaskf ((#fifo_qmaskb << 1) - 1) +.equ #fifo_qsize (1 << (#fifo_qlen + #fifo_qnum)) +fifo_queue: .skip 128 // #fifo_qsize + +// HOST PWR->HOST queue description +.equ #rfifo_qlen 4 // log2(size of queue entry in bytes) +.equ #rfifo_qnum 3 // log2(max number of entries in queue) +.equ #rfifo_qmaskb (1 << #rfifo_qnum) // max number of entries in queue +.equ #rfifo_qmaskp (#rfifo_qmaskb - 1) +.equ #rfifo_qmaskf ((#rfifo_qmaskb << 1) - 1) +.equ #rfifo_qsize (1 << (#rfifo_qlen + #rfifo_qnum)) +rfifo_queue: .skip 128 // #rfifo_qsize +#endif + +/****************************************************************************** + * HOST code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE +// HOST->PWR comms - dequeue message(s) for process(es) from FIFO +// +// $r15 - current (host) +// $r0 - zero +host_send: + nv_iord($r1, NV_PPWR_FIFO_GET(0)) + nv_iord($r2, NV_PPWR_FIFO_PUT(0)) + cmp b32 $r1 $r2 + bra e #host_send_done + // calculate address of message + and $r14 $r1 #fifo_qmaskp + shl b32 $r14 $r14 #fifo_qlen + add b32 $r14 #fifo_queue + + // read message data, and pass to appropriate process + ld b32 $r11 D[$r14 + #fifo_data1] + ld b32 $r12 D[$r14 + #fifo_data0] + ld b32 $r13 D[$r14 + #fifo_message] + ld b32 $r14 D[$r14 + #fifo_process] + call(send) + + // increment GET + add b32 $r1 0x1 + and $r14 $r1 #fifo_qmaskf + nv_iowr(NV_PPWR_FIFO_GET(0), $r14) + bra #host_send + host_send_done: + ret + +// PWR->HOST comms - enqueue message for HOST to RFIFO +// +// $r15 - current (host) +// $r14 - process +// $r13 - message +// $r12 - message data 0 +// $r11 - message data 1 +// $r0 - zero +host_recv: + // message from intr handler == HOST->PWR comms pending + imm32($r1, PROC_KERN) + cmp b32 $r14 $r1 + bra e #host_send + + // wait for space in RFIFO + host_recv_wait: + nv_iord($r1, NV_PPWR_RFIFO_GET) + nv_iord($r2, NV_PPWR_RFIFO_PUT) + xor $r1 #rfifo_qmaskb + cmp b32 $r1 $r2 + bra e #host_recv_wait + + and $r3 $r2 #rfifo_qmaskp + shl b32 $r3 #rfifo_qlen + add b32 $r3 #rfifo_queue + + // enqueue message + st b32 D[$r3 + #fifo_data1] $r11 + st b32 D[$r3 + #fifo_data0] $r12 + st b32 D[$r3 + #fifo_message] $r13 + st b32 D[$r3 + #fifo_process] $r14 + + add b32 $r2 0x1 + and $r2 #rfifo_qmaskf + nv_iowr(NV_PPWR_RFIFO_PUT, $r2) + + // notify host of pending message + mov $r2 NV_PPWR_INTR_TRIGGER_USER0 + nv_iowr(NV_PPWR_INTR_TRIGGER, $r2) + ret + +// $r15 - current (host) +// $r0 - zero +host_init: + // store each fifo's base/size in H2D/D2H scratch regs + mov $r1 #fifo_qsize + shl b32 $r1 16 + or $r1 #fifo_queue + nv_iowr(NV_PPWR_H2D, $r1); + + mov $r1 #rfifo_qsize + shl b32 $r1 16 + or $r1 #rfifo_queue + nv_iowr(NV_PPWR_D2H, $r1); + + // enable fifo subintr for first fifo + mov $r1 1 + nv_iowr(NV_PPWR_FIFO_INTR_EN, $r1) + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc new file mode 100644 index 000000000..757dda700 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc @@ -0,0 +1,393 @@ +/* + * 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 + */ + +#define T_TIMEOUT 2200000 +#define T_RISEFALL 1000 +#define T_HOLD 5000 + +#ifdef INCLUDE_PROC +process(PROC_I2C_, #i2c_init, #i2c_recv) +#endif + +/****************************************************************************** + * I2C_ data segment + *****************************************************************************/ +#ifdef INCLUDE_DATA +i2c_scl_map: +.b32 NV_PPWR_OUTPUT_I2C_0_SCL +.b32 NV_PPWR_OUTPUT_I2C_1_SCL +.b32 NV_PPWR_OUTPUT_I2C_2_SCL +.b32 NV_PPWR_OUTPUT_I2C_3_SCL +.b32 NV_PPWR_OUTPUT_I2C_4_SCL +.b32 NV_PPWR_OUTPUT_I2C_5_SCL +.b32 NV_PPWR_OUTPUT_I2C_6_SCL +.b32 NV_PPWR_OUTPUT_I2C_7_SCL +.b32 NV_PPWR_OUTPUT_I2C_8_SCL +.b32 NV_PPWR_OUTPUT_I2C_9_SCL +i2c_sda_map: +.b32 NV_PPWR_OUTPUT_I2C_0_SDA +.b32 NV_PPWR_OUTPUT_I2C_1_SDA +.b32 NV_PPWR_OUTPUT_I2C_2_SDA +.b32 NV_PPWR_OUTPUT_I2C_3_SDA +.b32 NV_PPWR_OUTPUT_I2C_4_SDA +.b32 NV_PPWR_OUTPUT_I2C_5_SDA +.b32 NV_PPWR_OUTPUT_I2C_6_SDA +.b32 NV_PPWR_OUTPUT_I2C_7_SDA +.b32 NV_PPWR_OUTPUT_I2C_8_SDA +.b32 NV_PPWR_OUTPUT_I2C_9_SDA +#if NVKM_PPWR_CHIPSET < GF119 +i2c_ctrl: +.b32 0x00e138 +.b32 0x00e150 +.b32 0x00e168 +.b32 0x00e180 +.b32 0x00e254 +.b32 0x00e274 +.b32 0x00e764 +.b32 0x00e780 +.b32 0x00e79c +.b32 0x00e7b8 +#endif +#endif + +/****************************************************************************** + * I2C_ code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE + +// $r3 - value +// $r2 - sda line +// $r1 - scl line +// $r0 - zero +i2c_drive_scl: + cmp b32 $r3 0 + bra e #i2c_drive_scl_lo + nv_iowr(NV_PPWR_OUTPUT_SET, $r1) + ret + i2c_drive_scl_lo: + nv_iowr(NV_PPWR_OUTPUT_CLR, $r1) + ret + +i2c_drive_sda: + cmp b32 $r3 0 + bra e #i2c_drive_sda_lo + nv_iowr(NV_PPWR_OUTPUT_SET, $r2) + ret + i2c_drive_sda_lo: + nv_iowr(NV_PPWR_OUTPUT_CLR, $r2) + ret + +i2c_sense_scl: + bclr $flags $p1 + nv_iord($r3, NV_PPWR_INPUT) + and $r3 $r1 + bra z #i2c_sense_scl_done + bset $flags $p1 + i2c_sense_scl_done: + ret + +i2c_sense_sda: + bclr $flags $p1 + nv_iord($r3, NV_PPWR_INPUT) + and $r3 $r2 + bra z #i2c_sense_sda_done + bset $flags $p1 + i2c_sense_sda_done: + ret + +#define i2c_drive_scl(v) /* +*/ mov $r3 (v) /* +*/ call(i2c_drive_scl) +#define i2c_drive_sda(v) /* +*/ mov $r3 (v) /* +*/ call(i2c_drive_sda) +#define i2c_sense_scl() /* +*/ call(i2c_sense_scl) +#define i2c_sense_sda() /* +*/ call(i2c_sense_sda) +#define i2c_delay(v) /* +*/ mov $r14 (v) /* +*/ call(nsec) + +#define i2c_trace_init() /* +*/ imm32($r6, 0x10000000) /* +*/ sub b32 $r7 $r6 1 /* +*/ +#define i2c_trace_down() /* +*/ shr b32 $r6 4 /* +*/ push $r5 /* +*/ shl b32 $r5 $r6 4 /* +*/ sub b32 $r5 $r6 /* +*/ not b32 $r5 /* +*/ and $r7 $r5 /* +*/ pop $r5 /* +*/ +#define i2c_trace_exit() /* +*/ shl b32 $r6 4 /* +*/ +#define i2c_trace_next() /* +*/ add b32 $r7 $r6 /* +*/ +#define i2c_trace_call(func) /* +*/ i2c_trace_next() /* +*/ i2c_trace_down() /* +*/ call(func) /* +*/ i2c_trace_exit() /* +*/ + +i2c_raise_scl: + push $r4 + mov $r4 (T_TIMEOUT / T_RISEFALL) + i2c_drive_scl(1) + i2c_raise_scl_wait: + i2c_delay(T_RISEFALL) + i2c_sense_scl() + bra $p1 #i2c_raise_scl_done + sub b32 $r4 1 + bra nz #i2c_raise_scl_wait + i2c_raise_scl_done: + pop $r4 + ret + +i2c_start: + i2c_sense_scl() + bra not $p1 #i2c_start_rep + i2c_sense_sda() + bra not $p1 #i2c_start_rep + bra #i2c_start_send + i2c_start_rep: + i2c_drive_scl(0) + i2c_drive_sda(1) + i2c_trace_call(i2c_raise_scl) + bra not $p1 #i2c_start_out + i2c_start_send: + i2c_drive_sda(0) + i2c_delay(T_HOLD) + i2c_drive_scl(0) + i2c_delay(T_HOLD) + i2c_start_out: + ret + +i2c_stop: + i2c_drive_scl(0) + i2c_drive_sda(0) + i2c_delay(T_RISEFALL) + i2c_drive_scl(1) + i2c_delay(T_HOLD) + i2c_drive_sda(1) + i2c_delay(T_HOLD) + ret + +// $r3 - value +// $r2 - sda line +// $r1 - scl line +// $r0 - zero +i2c_bitw: + call(i2c_drive_sda) + i2c_delay(T_RISEFALL) + i2c_trace_call(i2c_raise_scl) + bra not $p1 #i2c_bitw_out + i2c_delay(T_HOLD) + i2c_drive_scl(0) + i2c_delay(T_HOLD) + i2c_bitw_out: + ret + +// $r3 - value (out) +// $r2 - sda line +// $r1 - scl line +// $r0 - zero +i2c_bitr: + i2c_drive_sda(1) + i2c_delay(T_RISEFALL) + i2c_trace_call(i2c_raise_scl) + bra not $p1 #i2c_bitr_done + i2c_sense_sda() + i2c_drive_scl(0) + i2c_delay(T_HOLD) + xbit $r3 $flags $p1 + bset $flags $p1 + i2c_bitr_done: + ret + +i2c_get_byte: + mov $r5 0 + mov $r4 8 + i2c_get_byte_next: + shl b32 $r5 1 + i2c_trace_call(i2c_bitr) + bra not $p1 #i2c_get_byte_done + or $r5 $r3 + sub b32 $r4 1 + bra nz #i2c_get_byte_next + mov $r3 1 + i2c_trace_call(i2c_bitw) + i2c_get_byte_done: + ret + +i2c_put_byte: + mov $r4 8 + i2c_put_byte_next: + sub b32 $r4 1 + xbit $r3 $r5 $r4 + i2c_trace_call(i2c_bitw) + bra not $p1 #i2c_put_byte_done + cmp b32 $r4 0 + bra ne #i2c_put_byte_next + i2c_trace_call(i2c_bitr) + bra not $p1 #i2c_put_byte_done + i2c_trace_next() + cmp b32 $r3 1 + bra ne #i2c_put_byte_done + bclr $flags $p1 // nack + i2c_put_byte_done: + ret + +i2c_addr: + i2c_trace_call(i2c_start) + bra not $p1 #i2c_addr_done + extr $r3 $r12 I2C__MSG_DATA0_ADDR + shl b32 $r3 1 + or $r5 $r3 + i2c_trace_call(i2c_put_byte) + i2c_addr_done: + ret + +i2c_acquire_addr: + extr $r14 $r12 I2C__MSG_DATA0_PORT +#if NVKM_PPWR_CHIPSET < GF119 + shl b32 $r14 2 + add b32 $r14 #i2c_ctrl + ld b32 $r14 D[$r14] +#else + shl b32 $r14 5 + add b32 $r14 0x00d014 +#endif + ret + +i2c_acquire: + call(i2c_acquire_addr) + call(rd32) + bset $r13 3 + call(wr32) + ret + +i2c_release: + call(i2c_acquire_addr) + call(rd32) + bclr $r13 3 + call(wr32) + ret + +// description +// +// $r15 - current (i2c) +// $r14 - sender process name +// $r13 - message +// $r12 - data0 +// $r11 - data1 +// $r0 - zero +i2c_recv: + bclr $flags $p1 + extr $r1 $r12 I2C__MSG_DATA0_PORT + shl b32 $r1 2 + cmp b32 $r1 (#i2c_sda_map - #i2c_scl_map) + bra ge #i2c_recv_done + add b32 $r3 $r1 #i2c_sda_map + ld b32 $r2 D[$r3] + add b32 $r3 $r1 #i2c_scl_map + ld b32 $r1 D[$r3] + + bset $flags $p2 + push $r13 + push $r14 + + push $r13 + i2c_trace_init() + i2c_trace_call(i2c_acquire) + pop $r13 + + cmp b32 $r13 I2C__MSG_RD08 + bra ne #i2c_recv_not_rd08 + mov $r5 0 + i2c_trace_call(i2c_addr) + bra not $p1 #i2c_recv_done + extr $r5 $r12 I2C__MSG_DATA0_RD08_REG + i2c_trace_call(i2c_put_byte) + bra not $p1 #i2c_recv_done + mov $r5 1 + i2c_trace_call(i2c_addr) + bra not $p1 #i2c_recv_done + i2c_trace_call(i2c_get_byte) + bra not $p1 #i2c_recv_done + ins $r11 $r5 I2C__MSG_DATA1_RD08_VAL + i2c_trace_call(i2c_stop) + mov b32 $r11 $r5 + clear b32 $r7 + bra #i2c_recv_done + + i2c_recv_not_rd08: + cmp b32 $r13 I2C__MSG_WR08 + bra ne #i2c_recv_not_wr08 + mov $r5 0 + call(i2c_addr) + bra not $p1 #i2c_recv_done + extr $r5 $r12 I2C__MSG_DATA0_WR08_REG + call(i2c_put_byte) + bra not $p1 #i2c_recv_done + mov $r5 0 + call(i2c_addr) + bra not $p1 #i2c_recv_done + extr $r5 $r11 I2C__MSG_DATA1_WR08_VAL + call(i2c_put_byte) + bra not $p1 #i2c_recv_done + call(i2c_stop) + clear b32 $r7 + extr $r5 $r12 I2C__MSG_DATA0_WR08_SYNC + bra nz #i2c_recv_done + bclr $flags $p2 + bra #i2c_recv_done + + i2c_recv_not_wr08: + + i2c_recv_done: + extr $r14 $r12 I2C__MSG_DATA0_PORT + call(i2c_release) + + pop $r14 + pop $r13 + bra not $p2 #i2c_recv_exit + mov b32 $r12 $r7 + call(send) + + i2c_recv_exit: + ret + +// description +// +// $r15 - current (i2c) +// $r0 - zero +i2c_init: + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc new file mode 100644 index 000000000..98f1c3738 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc @@ -0,0 +1,84 @@ +/* + * 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 + */ + +#ifdef INCLUDE_PROC +process(PROC_IDLE, #idle, #idle_recv) +#endif + +/****************************************************************************** + * IDLE data segment + *****************************************************************************/ +#ifdef INCLUDE_DATA +#endif + +/****************************************************************************** + * IDLE code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE +// description +// +// $r15 - current (idle) +// $r14 - message +// $r0 - zero +idle_recv: + ret + +// description +// +// $r15 - current (idle) +// $r0 - zero +idle: + // set our "no interrupt has occurred during our execution" flag + bset $flags $p0 + + // count IDLE invocations for debugging purposes + nv_iord($r1, NV_PPWR_DSCRATCH(1)) + add b32 $r1 1 + nv_iowr(NV_PPWR_DSCRATCH(1), $r1) + + // keep looping while there's pending messages for any process + idle_loop: + mov $r1 #proc_list_head + bclr $flags $p2 + idle_proc: + // process the process' messages until there's none left + idle_proc_exec: + push $r1 + mov b32 $r14 $r1 + call(recv) + pop $r1 + bra not $p1 #idle_proc_next + bset $flags $p2 + bra #idle_proc_exec + // next process! + idle_proc_next: + add b32 $r1 #proc_size + cmp b32 $r1 $r15 + bra ne #idle_proc + bra $p2 #idle_loop + + // sleep if no interrupts have occurred + sleep $p0 + bra #idle +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc new file mode 100644 index 000000000..c20a3bd33 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc @@ -0,0 +1,544 @@ +/* + * 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 + */ + +/****************************************************************************** + * kernel data segment + *****************************************************************************/ +#ifdef INCLUDE_PROC +proc_kern: +process(PROC_KERN, 0, 0) +proc_list_head: +#endif + +#ifdef INCLUDE_DATA +proc_list_tail: +time_prev: .b32 0 +time_next: .b32 0 +#endif + +/****************************************************************************** + * kernel code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE + bra #init + +// read nv register +// +// $r15 - current +// $r14 - addr +// $r13 - data (return) +// $r0 - zero +rd32: + nv_iowr(NV_PPWR_MMIO_ADDR, $r14) + imm32($r13, NV_PPWR_MMIO_CTRL_OP_RD | NV_PPWR_MMIO_CTRL_TRIGGER) + nv_iowr(NV_PPWR_MMIO_CTRL, $r13) + rd32_wait: + nv_iord($r13, NV_PPWR_MMIO_CTRL) + and $r13 NV_PPWR_MMIO_CTRL_STATUS + bra nz #rd32_wait + nv_iord($r13, NV_PPWR_MMIO_DATA) + ret + +// write nv register +// +// $r15 - current +// $r14 - addr +// $r13 - data +// $r0 - zero +wr32: + nv_iowr(NV_PPWR_MMIO_ADDR, $r14) + nv_iowr(NV_PPWR_MMIO_DATA, $r13) + imm32($r13, NV_PPWR_MMIO_CTRL_OP_WR | NV_PPWR_MMIO_CTRL_MASK_B32_0 | NV_PPWR_MMIO_CTRL_TRIGGER) + +#ifdef NVKM_FALCON_MMIO_TRAP + push $r13 + mov $r13 NV_PPWR_INTR_TRIGGER_USER1 + nv_iowr(NV_PPWR_INTR_TRIGGER, $r13) + wr32_host: + nv_iord($r13, NV_PPWR_INTR) + and $r13 NV_PPWR_INTR_USER1 + bra nz #wr32_host + pop $r13 +#endif + + nv_iowr(NV_PPWR_MMIO_CTRL, $r13) + wr32_wait: + nv_iord($r13, NV_PPWR_MMIO_CTRL) + and $r13 NV_PPWR_MMIO_CTRL_STATUS + bra nz #wr32_wait + ret + +// busy-wait for a period of time +// +// $r15 - current +// $r14 - ns +// $r0 - zero +nsec: + push $r9 + push $r8 + nv_iord($r8, NV_PPWR_TIMER_LOW) + nsec_loop: + nv_iord($r9, NV_PPWR_TIMER_LOW) + sub b32 $r9 $r8 + cmp b32 $r9 $r14 + bra l #nsec_loop + pop $r8 + pop $r9 + ret + +// busy-wait for a period of time +// +// $r15 - current +// $r14 - addr +// $r13 - mask +// $r12 - data +// $r11 - timeout (ns) +// $r0 - zero +wait: + push $r9 + push $r8 + nv_iord($r8, NV_PPWR_TIMER_LOW) + wait_loop: + nv_rd32($r10, $r14) + and $r10 $r13 + cmp b32 $r10 $r12 + bra e #wait_done + nv_iord($r9, NV_PPWR_TIMER_LOW) + sub b32 $r9 $r8 + cmp b32 $r9 $r11 + bra l #wait_loop + wait_done: + pop $r8 + pop $r9 + ret + +// $r15 - current (kern) +// $r14 - process +// $r8 - NV_PPWR_INTR +intr_watchdog: + // read process' timer status, skip if not enabled + ld b32 $r9 D[$r14 + #proc_time] + cmp b32 $r9 0 + bra z #intr_watchdog_next_proc + + // subtract last timer's value from process' timer, + // if it's <= 0 then the timer has expired + ld b32 $r10 D[$r0 + #time_prev] + sub b32 $r9 $r10 + bra g #intr_watchdog_next_time + mov $r13 KMSG_ALARM + call(send_proc) + clear b32 $r9 + bra #intr_watchdog_next_proc + + // otherwise, update the next timer's value if this + // process' timer is the soonest + intr_watchdog_next_time: + // ... or if there's no next timer yet + ld b32 $r10 D[$r0 + #time_next] + cmp b32 $r10 0 + bra z #intr_watchdog_next_time_set + + cmp b32 $r9 $r10 + bra g #intr_watchdog_next_proc + intr_watchdog_next_time_set: + st b32 D[$r0 + #time_next] $r9 + + // update process' timer status, and advance + intr_watchdog_next_proc: + st b32 D[$r14 + #proc_time] $r9 + add b32 $r14 #proc_size + cmp b32 $r14 #proc_list_tail + bra ne #intr_watchdog + ret + +intr: + push $r0 + clear b32 $r0 + push $r8 + push $r9 + push $r10 + push $r11 + push $r12 + push $r13 + push $r14 + push $r15 + mov $r15 #proc_kern + mov $r8 $flags + push $r8 + + nv_iord($r8, NV_PPWR_DSCRATCH(0)) + add b32 $r8 1 + nv_iowr(NV_PPWR_DSCRATCH(0), $r8) + + nv_iord($r8, NV_PPWR_INTR) + and $r9 $r8 NV_PPWR_INTR_WATCHDOG + bra z #intr_skip_watchdog + st b32 D[$r0 + #time_next] $r0 + mov $r14 #proc_list_head + call(intr_watchdog) + ld b32 $r9 D[$r0 + #time_next] + cmp b32 $r9 0 + bra z #intr_skip_watchdog + nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9) + st b32 D[$r0 + #time_prev] $r9 + + intr_skip_watchdog: + and $r9 $r8 NV_PPWR_INTR_SUBINTR + bra z #intr_skip_subintr + nv_iord($r9, NV_PPWR_SUBINTR) + and $r10 $r9 NV_PPWR_SUBINTR_FIFO + bra z #intr_subintr_skip_fifo + nv_iord($r12, NV_PPWR_FIFO_INTR) + push $r12 + imm32($r14, PROC_HOST) + mov $r13 KMSG_FIFO + call(send) + pop $r12 + nv_iowr(NV_PPWR_FIFO_INTR, $r12) + intr_subintr_skip_fifo: + nv_iowr(NV_PPWR_SUBINTR, $r9) + + intr_skip_subintr: + mov $r9 (NV_PPWR_INTR_USER0 | NV_PPWR_INTR_USER1 | NV_PPWR_INTR_PAUSE) + not b32 $r9 + and $r8 $r9 + nv_iowr(NV_PPWR_INTR_ACK, $r8) + + pop $r8 + mov $flags $r8 + pop $r15 + pop $r14 + pop $r13 + pop $r12 + pop $r11 + pop $r10 + pop $r9 + pop $r8 + pop $r0 + bclr $flags $p0 + iret + +// calculate the number of ticks in the specified nanoseconds delay +// +// $r15 - current +// $r14 - ns +// $r14 - ticks (return) +// $r0 - zero +ticks_from_ns: + push $r12 + push $r11 + + /* try not losing precision (multiply then divide) */ + imm32($r13, HW_TICKS_PER_US) + call(mulu32_32_64) + + /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */ + div $r12 $r12 1000 + + /* check if there wasn't any overflow */ + cmpu b32 $r11 0 + bra e #ticks_from_ns_quit + + /* let's divide then multiply, too bad for the precision! */ + div $r14 $r14 1000 + imm32($r13, HW_TICKS_PER_US) + call(mulu32_32_64) + + /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */ + +ticks_from_ns_quit: + mov b32 $r14 $r12 + pop $r11 + pop $r12 + ret + +// calculate the number of ticks in the specified microsecond delay +// +// $r15 - current +// $r14 - us +// $r14 - ticks (return) +// $r0 - zero +ticks_from_us: + push $r12 + push $r11 + + /* simply multiply $us by HW_TICKS_PER_US */ + imm32($r13, HW_TICKS_PER_US) + call(mulu32_32_64) + mov b32 $r14 $r12 + + /* check if there wasn't any overflow */ + cmpu b32 $r11 0 + bra e #ticks_from_us_quit + + /* Overflow! */ + clear b32 $r14 + +ticks_from_us_quit: + pop $r11 + pop $r12 + ret + +// calculate the number of ticks in the specified microsecond delay +// +// $r15 - current +// $r14 - ticks +// $r14 - us (return) +// $r0 - zero +ticks_to_us: + /* simply divide $ticks by HW_TICKS_PER_US */ + imm32($r13, HW_TICKS_PER_US) + div $r14 $r14 $r13 + + ret + +// request the current process be sent a message after a timeout expires +// +// $r15 - current +// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow) +// $r0 - zero +timer: + push $r9 + push $r8 + + // interrupts off to prevent racing with timer isr + bclr $flags ie0 + + // if current process already has a timer set, bail + ld b32 $r8 D[$r15 + #proc_time] + cmp b32 $r8 0 + bra g #timer_done + + // halt watchdog timer temporarily + clear b32 $r8 + nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) + + // find out how much time elapsed since the last update + // of the watchdog and add this time to the wanted ticks + nv_iord($r8, NV_PPWR_WATCHDOG_TIME) + ld b32 $r9 D[$r0 + #time_prev] + sub b32 $r9 $r8 + add b32 $r14 $r9 + st b32 D[$r15 + #proc_time] $r14 + + // check for a pending interrupt. if there's one already + // pending, we can just bail since the timer isr will + // queue the next soonest right after it's done + nv_iord($r8, NV_PPWR_INTR) + and $r8 NV_PPWR_INTR_WATCHDOG + bra nz #timer_enable + + // update the watchdog if this timer should expire first, + // or if there's no timeout already set + nv_iord($r8, NV_PPWR_WATCHDOG_TIME) + cmp b32 $r14 $r0 + bra e #timer_reset + cmp b32 $r14 $r8 + bra g #timer_enable + timer_reset: + nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14) + st b32 D[$r0 + #time_prev] $r14 + + // re-enable the watchdog timer + timer_enable: + mov $r8 1 + nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) + + // interrupts back on + timer_done: + bset $flags ie0 + + pop $r8 + pop $r9 + ret + +// send message to another process +// +// $r15 - current +// $r14 - process +// $r13 - message +// $r12 - message data 0 +// $r11 - message data 1 +// $r0 - zero +send_proc: + push $r8 + push $r9 + // check for space in queue + ld b32 $r8 D[$r14 + #proc_qget] + ld b32 $r9 D[$r14 + #proc_qput] + xor $r8 #proc_qmaskb + cmp b32 $r8 $r9 + bra e #send_done + + // enqueue message + and $r8 $r9 #proc_qmaskp + shl b32 $r8 $r8 #proc_qlen + add b32 $r8 #proc_queue + add b32 $r8 $r14 + + ld b32 $r10 D[$r15 + #proc_id] + st b32 D[$r8 + #msg_process] $r10 + st b32 D[$r8 + #msg_message] $r13 + st b32 D[$r8 + #msg_data0] $r12 + st b32 D[$r8 + #msg_data1] $r11 + + // increment PUT + add b32 $r9 1 + and $r9 #proc_qmaskf + st b32 D[$r14 + #proc_qput] $r9 + bset $flags $p2 + send_done: + pop $r9 + pop $r8 + ret + +// lookup process structure by its name +// +// $r15 - current +// $r14 - process name +// $r0 - zero +// +// $r14 - process +// $p1 - success +find: + push $r8 + mov $r8 #proc_list_head + bset $flags $p1 + find_loop: + ld b32 $r10 D[$r8 + #proc_id] + cmp b32 $r10 $r14 + bra e #find_done + add b32 $r8 #proc_size + cmp b32 $r8 #proc_list_tail + bra ne #find_loop + bclr $flags $p1 + find_done: + mov b32 $r14 $r8 + pop $r8 + ret + +// send message to another process +// +// $r15 - current +// $r14 - process id +// $r13 - message +// $r12 - message data 0 +// $r11 - message data 1 +// $r0 - zero +send: + call(find) + bra $p1 #send_proc + ret + +// process single message for a given process +// +// $r15 - current +// $r14 - process +// $r0 - zero +recv: + push $r9 + push $r8 + + ld b32 $r8 D[$r14 + #proc_qget] + ld b32 $r9 D[$r14 + #proc_qput] + bclr $flags $p1 + cmp b32 $r8 $r9 + bra e #recv_done + // dequeue message + and $r9 $r8 #proc_qmaskp + add b32 $r8 1 + and $r8 #proc_qmaskf + st b32 D[$r14 + #proc_qget] $r8 + ld b32 $r10 D[$r14 + #proc_recv] + + push $r15 + mov $r15 $flags + push $r15 + mov b32 $r15 $r14 + + shl b32 $r9 $r9 #proc_qlen + add b32 $r14 $r9 + add b32 $r14 #proc_queue + ld b32 $r11 D[$r14 + #msg_data1] + ld b32 $r12 D[$r14 + #msg_data0] + ld b32 $r13 D[$r14 + #msg_message] + ld b32 $r14 D[$r14 + #msg_process] + + // process it + call $r10 + pop $r15 + mov $flags $r15 + bset $flags $p1 + pop $r15 + recv_done: + pop $r8 + pop $r9 + ret + +init: + // setup stack + nv_iord($r1, NV_PPWR_CAPS) + extr $r1 $r1 9:17 + shl b32 $r1 8 + mov $sp $r1 + +#ifdef NVKM_FALCON_MMIO_UAS + // somehow allows the magic "access mmio via D[]" stuff that's + // used by the nv_rd32/nv_wr32 macros to work + imm32($r1, 0x10 | NV_PPWR_UAS_CONFIG_ENABLE) + nv_iowrs(NV_PPWR_UAS_CONFIG, $r1) +#endif + + // route all interrupts except user0/1 and pause to fuc + imm32($r1, 0xe0) + nv_iowr(NV_PPWR_INTR_ROUTE, $r1) + + // enable watchdog and subintr intrs + mov $r1 NV_PPWR_INTR_EN_CLR_MASK + nv_iowr(NV_PPWR_INTR_EN_CLR, $r1) + mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG + or $r1 NV_PPWR_INTR_EN_SET_SUBINTR + nv_iowr(NV_PPWR_INTR_EN_SET, $r1) + + // enable interrupts globally + imm32($r1, #intr) + and $r1 0xffff + mov $iv0 $r1 + bset $flags ie0 + + // enable watchdog timer + mov $r1 1 + nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1) + + // bootstrap processes, idle process will be last, and not return + mov $r15 #proc_list_head + init_proc: + ld b32 $r1 D[$r15 + #proc_init] + cmp b32 $r1 0 + bra z #init_proc + call $r1 + add b32 $r15 #proc_size + bra #init_proc +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc new file mode 100644 index 000000000..3737bd27f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc @@ -0,0 +1,272 @@ +/* + * 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 + */ + +#define GT215 0xa3 +#define GF100 0xc0 +#define GF119 0xd9 +#define GK208 0x108 + +#include "os.h" + +// IO addresses +#define NV_PPWR_INTR_TRIGGER 0x0000 +#define NV_PPWR_INTR_TRIGGER_USER1 0x00000080 +#define NV_PPWR_INTR_TRIGGER_USER0 0x00000040 +#define NV_PPWR_INTR_ACK 0x0004 +#define NV_PPWR_INTR_ACK_SUBINTR 0x00000800 +#define NV_PPWR_INTR_ACK_WATCHDOG 0x00000002 +#define NV_PPWR_INTR 0x0008 +#define NV_PPWR_INTR_SUBINTR 0x00000800 +#define NV_PPWR_INTR_USER1 0x00000080 +#define NV_PPWR_INTR_USER0 0x00000040 +#define NV_PPWR_INTR_PAUSE 0x00000020 +#define NV_PPWR_INTR_WATCHDOG 0x00000002 +#define NV_PPWR_INTR_EN_SET 0x0010 +#define NV_PPWR_INTR_EN_SET_SUBINTR 0x00000800 +#define NV_PPWR_INTR_EN_SET_WATCHDOG 0x00000002 +#define NV_PPWR_INTR_EN_CLR 0x0014 +#define NV_PPWR_INTR_EN_CLR_MASK /* fuck i hate envyas */ -1 +#define NV_PPWR_INTR_ROUTE 0x001c +#define NV_PPWR_TIMER_LOW 0x002c +#define NV_PPWR_WATCHDOG_TIME 0x0034 +#define NV_PPWR_WATCHDOG_ENABLE 0x0038 +#define NV_PPWR_CAPS 0x0108 +#define NV_PPWR_UAS_CONFIG 0x0164 +#define NV_PPWR_UAS_CONFIG_ENABLE 0x00010000 +#if NVKM_PPWR_CHIPSET >= GK208 +#define NV_PPWR_DSCRATCH(i) (4 * (i) + 0x0450) +#endif +#define NV_PPWR_FIFO_PUT(i) (4 * (i) + 0x04a0) +#define NV_PPWR_FIFO_GET(i) (4 * (i) + 0x04b0) +#define NV_PPWR_FIFO_INTR 0x04c0 +#define NV_PPWR_FIFO_INTR_EN 0x04c4 +#define NV_PPWR_RFIFO_PUT 0x04c8 +#define NV_PPWR_RFIFO_GET 0x04cc +#define NV_PPWR_H2D 0x04d0 +#define NV_PPWR_D2H 0x04dc +#if NVKM_PPWR_CHIPSET < GK208 +#define NV_PPWR_DSCRATCH(i) (4 * (i) + 0x05d0) +#endif +#define NV_PPWR_SUBINTR 0x0688 +#define NV_PPWR_SUBINTR_FIFO 0x00000002 +#define NV_PPWR_MMIO_ADDR 0x07a0 +#define NV_PPWR_MMIO_DATA 0x07a4 +#define NV_PPWR_MMIO_CTRL 0x07ac +#define NV_PPWR_MMIO_CTRL_TRIGGER 0x00010000 +#define NV_PPWR_MMIO_CTRL_STATUS 0x00007000 +#define NV_PPWR_MMIO_CTRL_STATUS_IDLE 0x00000000 +#define NV_PPWR_MMIO_CTRL_MASK 0x000000f0 +#define NV_PPWR_MMIO_CTRL_MASK_B32_0 0x000000f0 +#define NV_PPWR_MMIO_CTRL_OP 0x00000003 +#define NV_PPWR_MMIO_CTRL_OP_RD 0x00000001 +#define NV_PPWR_MMIO_CTRL_OP_WR 0x00000002 +#define NV_PPWR_OUTPUT 0x07c0 +#define NV_PPWR_OUTPUT_FB_PAUSE 0x00000004 +#if NVKM_PPWR_CHIPSET < GF119 +#define NV_PPWR_OUTPUT_I2C_3_SCL 0x00000100 +#define NV_PPWR_OUTPUT_I2C_3_SDA 0x00000200 +#define NV_PPWR_OUTPUT_I2C_0_SCL 0x00001000 +#define NV_PPWR_OUTPUT_I2C_0_SDA 0x00002000 +#define NV_PPWR_OUTPUT_I2C_1_SCL 0x00004000 +#define NV_PPWR_OUTPUT_I2C_1_SDA 0x00008000 +#define NV_PPWR_OUTPUT_I2C_2_SCL 0x00010000 +#define NV_PPWR_OUTPUT_I2C_2_SDA 0x00020000 +#define NV_PPWR_OUTPUT_I2C_4_SCL 0x00040000 +#define NV_PPWR_OUTPUT_I2C_4_SDA 0x00080000 +#define NV_PPWR_OUTPUT_I2C_5_SCL 0x00100000 +#define NV_PPWR_OUTPUT_I2C_5_SDA 0x00200000 +#define NV_PPWR_OUTPUT_I2C_6_SCL 0x00400000 +#define NV_PPWR_OUTPUT_I2C_6_SDA 0x00800000 +#define NV_PPWR_OUTPUT_I2C_7_SCL 0x01000000 +#define NV_PPWR_OUTPUT_I2C_7_SDA 0x02000000 +#define NV_PPWR_OUTPUT_I2C_8_SCL 0x04000000 +#define NV_PPWR_OUTPUT_I2C_8_SDA 0x08000000 +#define NV_PPWR_OUTPUT_I2C_9_SCL 0x10000000 +#define NV_PPWR_OUTPUT_I2C_9_SDA 0x20000000 +#else +#define NV_PPWR_OUTPUT_I2C_0_SCL 0x00000400 +#define NV_PPWR_OUTPUT_I2C_1_SCL 0x00000800 +#define NV_PPWR_OUTPUT_I2C_2_SCL 0x00001000 +#define NV_PPWR_OUTPUT_I2C_3_SCL 0x00002000 +#define NV_PPWR_OUTPUT_I2C_4_SCL 0x00004000 +#define NV_PPWR_OUTPUT_I2C_5_SCL 0x00008000 +#define NV_PPWR_OUTPUT_I2C_6_SCL 0x00010000 +#define NV_PPWR_OUTPUT_I2C_7_SCL 0x00020000 +#define NV_PPWR_OUTPUT_I2C_8_SCL 0x00040000 +#define NV_PPWR_OUTPUT_I2C_9_SCL 0x00080000 +#define NV_PPWR_OUTPUT_I2C_0_SDA 0x00100000 +#define NV_PPWR_OUTPUT_I2C_1_SDA 0x00200000 +#define NV_PPWR_OUTPUT_I2C_2_SDA 0x00400000 +#define NV_PPWR_OUTPUT_I2C_3_SDA 0x00800000 +#define NV_PPWR_OUTPUT_I2C_4_SDA 0x01000000 +#define NV_PPWR_OUTPUT_I2C_5_SDA 0x02000000 +#define NV_PPWR_OUTPUT_I2C_6_SDA 0x04000000 +#define NV_PPWR_OUTPUT_I2C_7_SDA 0x08000000 +#define NV_PPWR_OUTPUT_I2C_8_SDA 0x10000000 +#define NV_PPWR_OUTPUT_I2C_9_SDA 0x20000000 +#endif +#define NV_PPWR_INPUT 0x07c4 +#define NV_PPWR_OUTPUT_SET 0x07e0 +#define NV_PPWR_OUTPUT_SET_FB_PAUSE 0x00000004 +#define NV_PPWR_OUTPUT_CLR 0x07e4 +#define NV_PPWR_OUTPUT_CLR_FB_PAUSE 0x00000004 + +// Inter-process message format +.equ #msg_process 0x00 /* send() target, recv() sender */ +.equ #msg_message 0x04 +.equ #msg_data0 0x08 +.equ #msg_data1 0x0c + +// Kernel message IDs +#define KMSG_FIFO 0x00000000 +#define KMSG_ALARM 0x00000001 + +// Process message queue description +.equ #proc_qlen 4 // log2(size of queue entry in bytes) +.equ #proc_qnum 2 // log2(max number of entries in queue) +.equ #proc_qmaskb (1 << #proc_qnum) // max number of entries in queue +.equ #proc_qmaskp (#proc_qmaskb - 1) +.equ #proc_qmaskf ((#proc_qmaskb << 1) - 1) +.equ #proc_qsize (1 << (#proc_qlen + #proc_qnum)) + +// Process table entry +.equ #proc_id 0x00 +.equ #proc_init 0x04 +.equ #proc_recv 0x08 +.equ #proc_time 0x0c +.equ #proc_qput 0x10 +.equ #proc_qget 0x14 +.equ #proc_queue 0x18 +.equ #proc_size (0x18 + #proc_qsize) + +#define process(id,init,recv) /* +*/ .b32 id /* +*/ .b32 init /* +*/ .b32 recv /* +*/ .b32 0 /* +*/ .b32 0 /* +*/ .b32 0 /* +*/ .skip 64 + +#if NVKM_PPWR_CHIPSET < GK208 +#define imm32(reg,val) /* +*/ movw reg ((val) & 0x0000ffff) /* +*/ sethi reg ((val) & 0xffff0000) +#else +#define imm32(reg,val) /* +*/ mov reg (val) +#endif + +#ifndef NVKM_FALCON_UNSHIFTED_IO +#define nv_iord(reg,ior) /* +*/ mov reg ior /* +*/ shl b32 reg 6 /* +*/ iord reg I[reg + 0x000] +#else +#define nv_iord(reg,ior) /* +*/ mov reg ior /* +*/ iord reg I[reg + 0x000] +#endif + +#ifndef NVKM_FALCON_UNSHIFTED_IO +#define nv_iowr(ior,reg) /* +*/ mov $r0 ior /* +*/ shl b32 $r0 6 /* +*/ iowr I[$r0 + 0x000] reg /* +*/ clear b32 $r0 +#else +#define nv_iowr(ior,reg) /* +*/ mov $r0 ior /* +*/ iowr I[$r0 + 0x000] reg /* +*/ clear b32 $r0 +#endif + +#ifndef NVKM_FALCON_UNSHIFTED_IO +#define nv_iowrs(ior,reg) /* +*/ mov $r0 ior /* +*/ shl b32 $r0 6 /* +*/ iowrs I[$r0 + 0x000] reg /* +*/ clear b32 $r0 +#else +#define nv_iowrs(ior,reg) /* +*/ mov $r0 ior /* +*/ iowrs I[$r0 + 0x000] reg /* +*/ clear b32 $r0 +#endif + +#define hash # +#define fn(a) a +#ifndef NVKM_FALCON_PC24 +#define call(a) call fn(hash)a +#else +#define call(a) lcall fn(hash)a +#endif + +#ifndef NVKM_FALCON_MMIO_UAS +#define nv_rd32(reg,addr) /* +*/ mov b32 $r14 addr /* +*/ call(rd32) /* +*/ mov b32 reg $r13 +#else +#define nv_rd32(reg,addr) /* +*/ sethi $r0 0x14000000 /* +*/ or $r0 addr /* +*/ ld b32 reg D[$r0] /* +*/ clear b32 $r0 +#endif + +#if !defined(NVKM_FALCON_MMIO_UAS) || defined(NVKM_FALCON_MMIO_TRAP) +#define nv_wr32(addr,reg) /* +*/ push addr /* +*/ push reg /* +*/ pop $r13 /* +*/ pop $r14 /* +*/ call(wr32) +#else +#define nv_wr32(addr,reg) /* +*/ sethi $r0 0x14000000 /* +*/ or $r0 addr /* +*/ st b32 D[$r0] reg /* +*/ clear b32 $r0 +#endif + +#define st(size, addr, reg) /* +*/ imm32($r0, addr) /* +*/ st size D[$r0] reg /* +*/ clear b32 $r0 + +#define ld(size, reg, addr) /* +*/ imm32($r0, addr) /* +*/ ld size reg D[$r0] /* +*/ clear b32 $r0 + +// does a 64+64 -> 64 unsigned addition (C = A + B) +#define addu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /* +*/ add b32 reg_a_c_lo b_lo /* +*/ adc b32 reg_a_c_hi b_hi + +// does a 64+64 -> 64 substraction (C = A - B) +#define subu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /* +*/ sub b32 reg_a_c_lo b_lo /* +*/ sbb b32 reg_a_c_hi b_hi diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc new file mode 100644 index 000000000..1663bf943 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc @@ -0,0 +1,447 @@ +/* + * 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 + */ + +#ifdef INCLUDE_PROC +process(PROC_MEMX, #memx_init, #memx_recv) +#endif + +/****************************************************************************** + * MEMX data segment + *****************************************************************************/ +#ifdef INCLUDE_DATA +.equ #memx_opcode 0 +.equ #memx_header 2 +.equ #memx_length 4 +.equ #memx_func 8 + +#define handler(cmd,hdr,len,func) /* +*/ .b16 MEMX_##cmd /* +*/ .b16 hdr /* +*/ .b16 len /* +*/ .b16 0 /* +*/ .b32 func + +memx_func_head: +handler(ENTER , 0x0000, 0x0000, #memx_func_enter) +memx_func_next: +handler(LEAVE , 0x0000, 0x0000, #memx_func_leave) +handler(WR32 , 0x0000, 0x0002, #memx_func_wr32) +handler(WAIT , 0x0004, 0x0000, #memx_func_wait) +handler(DELAY , 0x0001, 0x0000, #memx_func_delay) +handler(VBLANK, 0x0001, 0x0000, #memx_func_wait_vblank) +handler(TRAIN , 0x0000, 0x0000, #memx_func_train) +memx_func_tail: + +.equ #memx_func_size #memx_func_next - #memx_func_head +.equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size + +memx_ts_start: +.b32 0 +memx_ts_end: +.b32 0 + +memx_data_head: +.skip 0x0800 +memx_data_tail: + +memx_train_head: +.skip 0x0100 +memx_train_tail: +#endif + +/****************************************************************************** + * MEMX code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE +// description +// +// $r15 - current (memx) +// $r4 - packet length +// $r3 - opcode desciption +// $r0 - zero +memx_func_enter: +#if NVKM_PPWR_CHIPSET == GT215 + mov $r8 0x1610 + nv_rd32($r7, $r8) + imm32($r6, 0xfffffffc) + and $r7 $r6 + mov $r6 0x2 + or $r7 $r6 + nv_wr32($r8, $r7) +#else + mov $r6 0x001620 + imm32($r7, ~0x00000aa2); + nv_rd32($r8, $r6) + and $r8 $r7 + nv_wr32($r6, $r8) + + imm32($r7, ~0x00000001) + nv_rd32($r8, $r6) + and $r8 $r7 + nv_wr32($r6, $r8) + + mov $r6 0x0026f0 + nv_rd32($r8, $r6) + and $r8 $r7 + nv_wr32($r6, $r8) +#endif + + mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE + nv_iowr(NV_PPWR_OUTPUT_SET, $r6) + memx_func_enter_wait: + nv_iord($r6, NV_PPWR_OUTPUT) + and $r6 NV_PPWR_OUTPUT_FB_PAUSE + bra z #memx_func_enter_wait + + nv_iord($r6, NV_PPWR_TIMER_LOW) + st b32 D[$r0 + #memx_ts_start] $r6 + ret + +// description +// +// $r15 - current (memx) +// $r4 - packet length +// $r3 - opcode desciption +// $r0 - zero +memx_func_leave: + nv_iord($r6, NV_PPWR_TIMER_LOW) + st b32 D[$r0 + #memx_ts_end] $r6 + + mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE + nv_iowr(NV_PPWR_OUTPUT_CLR, $r6) + memx_func_leave_wait: + nv_iord($r6, NV_PPWR_OUTPUT) + and $r6 NV_PPWR_OUTPUT_FB_PAUSE + bra nz #memx_func_leave_wait + +#if NVKM_PPWR_CHIPSET == GT215 + mov $r8 0x1610 + nv_rd32($r7, $r8) + imm32($r6, 0xffffffcc) + and $r7 $r6 + nv_wr32($r8, $r7) +#else + mov $r6 0x0026f0 + imm32($r7, 0x00000001) + nv_rd32($r8, $r6) + or $r8 $r7 + nv_wr32($r6, $r8) + + mov $r6 0x001620 + nv_rd32($r8, $r6) + or $r8 $r7 + nv_wr32($r6, $r8) + + imm32($r7, 0x00000aa2); + nv_rd32($r8, $r6) + or $r8 $r7 + nv_wr32($r6, $r8) +#endif + ret + +#if NVKM_PPWR_CHIPSET < GF119 +// description +// +// $r15 - current (memx) +// $r4 - packet length +// +00: head to wait for vblank on +// $r3 - opcode desciption +// $r0 - zero +memx_func_wait_vblank: + ld b32 $r6 D[$r1 + 0x00] + cmp b32 $r6 0x0 + bra z #memx_func_wait_vblank_head0 + cmp b32 $r6 0x1 + bra z #memx_func_wait_vblank_head1 + bra #memx_func_wait_vblank_fini + + memx_func_wait_vblank_head1: + mov $r7 0x20 + bra #memx_func_wait_vblank_0 + + memx_func_wait_vblank_head0: + mov $r7 0x8 + + memx_func_wait_vblank_0: + nv_iord($r6, NV_PPWR_INPUT) + and $r6 $r7 + bra nz #memx_func_wait_vblank_0 + + memx_func_wait_vblank_1: + nv_iord($r6, NV_PPWR_INPUT) + and $r6 $r7 + bra z #memx_func_wait_vblank_1 + + memx_func_wait_vblank_fini: + add b32 $r1 0x4 + ret + +#else + +// XXX: currently no-op +// +// $r15 - current (memx) +// $r4 - packet length +// +00: head to wait for vblank on +// $r3 - opcode desciption +// $r0 - zero +memx_func_wait_vblank: + add b32 $r1 0x4 + ret + +#endif + +// description +// +// $r15 - current (memx) +// $r4 - packet length +// +00*n: addr +// +04*n: data +// $r3 - opcode desciption +// $r0 - zero +memx_func_wr32: + ld b32 $r6 D[$r1 + 0x00] + ld b32 $r5 D[$r1 + 0x04] + add b32 $r1 0x08 + nv_wr32($r6, $r5) + sub b32 $r4 0x02 + bra nz #memx_func_wr32 + ret + +// description +// +// $r15 - current (memx) +// $r4 - packet length +// +00: addr +// +04: mask +// +08: data +// +0c: timeout (ns) +// $r3 - opcode desciption +// $r0 - zero +memx_func_wait: + nv_iord($r8, NV_PPWR_TIMER_LOW) + ld b32 $r14 D[$r1 + 0x00] + ld b32 $r13 D[$r1 + 0x04] + ld b32 $r12 D[$r1 + 0x08] + ld b32 $r11 D[$r1 + 0x0c] + add b32 $r1 0x10 + call(wait) + ret + +// description +// +// $r15 - current (memx) +// $r4 - packet length +// +00: time (ns) +// $r3 - opcode desciption +// $r0 - zero +memx_func_delay: + ld b32 $r14 D[$r1 + 0x00] + add b32 $r1 0x04 + call(nsec) + ret + +// description +// +// $r15 - current (memx) +// $r4 - packet length +// $r3 - opcode desciption +// $r0 - zero +memx_func_train: +#if NVKM_PPWR_CHIPSET == GT215 +// $r5 - outer loop counter +// $r6 - inner loop counter +// $r7 - entry counter (#memx_train_head + $r7) + mov $r5 0x3 + mov $r7 0x0 + +// Read random memory to wake up... things + imm32($r9, 0x700000) + nv_rd32($r8,$r9) + mov $r14 0x2710 + call(nsec) + + memx_func_train_loop_outer: + mulu $r8 $r5 0x101 + sethi $r8 0x02000000 + imm32($r9, 0x1111e0) + nv_wr32($r9, $r8) + push $r5 + + mov $r6 0x0 + memx_func_train_loop_inner: + mov $r8 0x1111 + mulu $r9 $r6 $r8 + shl b32 $r8 $r9 0x10 + or $r8 $r9 + imm32($r9, 0x100720) + nv_wr32($r9, $r8) + + imm32($r9, 0x100080) + nv_rd32($r8, $r9) + or $r8 $r8 0x20 + nv_wr32($r9, $r8) + + imm32($r9, 0x10053c) + imm32($r8, 0x80003002) + nv_wr32($r9, $r8) + + imm32($r14, 0x100560) + imm32($r13, 0x80000000) + add b32 $r12 $r13 0 + imm32($r11, 0x001e8480) + call(wait) + + // $r5 - inner inner loop counter + // $r9 - result + mov $r5 0 + imm32($r9, 0x8300ffff) + memx_func_train_loop_4x: + imm32($r10, 0x100080) + nv_rd32($r8, $r10) + imm32($r11, 0xffffffdf) + and $r8 $r11 + nv_wr32($r10, $r8) + + imm32($r10, 0x10053c) + imm32($r8, 0x80003002) + nv_wr32($r10, $r8) + + imm32($r14, 0x100560) + imm32($r13, 0x80000000) + mov b32 $r12 $r13 + imm32($r11, 0x00002710) + call(wait) + + nv_rd32($r13, $r14) + and $r9 $r9 $r13 + + add b32 $r5 1 + cmp b16 $r5 0x4 + bra l #memx_func_train_loop_4x + + add b32 $r10 $r7 #memx_train_head + st b32 D[$r10 + 0] $r9 + add b32 $r6 1 + add b32 $r7 4 + + cmp b16 $r6 0x10 + bra l #memx_func_train_loop_inner + + pop $r5 + add b32 $r5 1 + cmp b16 $r5 7 + bra l #memx_func_train_loop_outer + +#endif + ret + +// description +// +// $r15 - current (memx) +// $r14 - sender process name +// $r13 - message (exec) +// $r12 - head of script +// $r11 - tail of script +// $r0 - zero +memx_exec: + push $r14 + push $r13 + mov b32 $r1 $r12 + mov b32 $r2 $r11 + + memx_exec_next: + // fetch the packet header + ld b32 $r3 D[$r1] + add b32 $r1 4 + extr $r4 $r3 16:31 + extr $r3 $r3 0:15 + + // execute the opcode handler + sub b32 $r3 1 + mulu $r3 #memx_func_size + ld b32 $r5 D[$r3 + #memx_func_head + #memx_func] + call $r5 + + // keep going, if we haven't reached the end + cmp b32 $r1 $r2 + bra l #memx_exec_next + + // send completion reply + ld b32 $r11 D[$r0 + #memx_ts_start] + ld b32 $r12 D[$r0 + #memx_ts_end] + sub b32 $r12 $r11 + nv_iord($r11, NV_PPWR_INPUT) + pop $r13 + pop $r14 + call(send) + ret + +// description +// +// $r15 - current (memx) +// $r14 - sender process name +// $r13 - message +// $r12 - data0 +// $r11 - data1 +// $r0 - zero +memx_info: + cmp b16 $r12 0x1 + bra e #memx_info_train + + memx_info_data: + mov $r12 #memx_data_head + mov $r11 #memx_data_tail - #memx_data_head + bra #memx_info_send + + memx_info_train: + mov $r12 #memx_train_head + mov $r11 #memx_train_tail - #memx_train_head + + memx_info_send: + call(send) + ret + +// description +// +// $r15 - current (memx) +// $r14 - sender process name +// $r13 - message +// $r12 - data0 +// $r11 - data1 +// $r0 - zero +memx_recv: + cmp b32 $r13 MEMX_MSG_EXEC + bra e #memx_exec + cmp b32 $r13 MEMX_MSG_INFO + bra e #memx_info + ret + +// description +// +// $r15 - current (memx) +// $r0 - zero +memx_init: + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h new file mode 100644 index 000000000..0d5abf27e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PWR_OS_H__ +#define __NVKM_PWR_OS_H__ + +/* Process names */ +#define PROC_KERN 0x52544e49 +#define PROC_IDLE 0x454c4449 +#define PROC_HOST 0x54534f48 +#define PROC_MEMX 0x584d454d +#define PROC_PERF 0x46524550 +#define PROC_I2C_ 0x5f433249 +#define PROC_TEST 0x54534554 + +/* KERN: message identifiers */ +#define KMSG_FIFO 0x00000000 +#define KMSG_ALARM 0x00000001 + +/* MEMX: message identifiers */ +#define MEMX_MSG_INFO 0 +#define MEMX_MSG_EXEC 1 + +/* MEMX: info types */ +#define MEMX_INFO_DATA 0 +#define MEMX_INFO_TRAIN 1 + +/* MEMX: script opcode definitions */ +#define MEMX_ENTER 1 +#define MEMX_LEAVE 2 +#define MEMX_WR32 3 +#define MEMX_WAIT 4 +#define MEMX_DELAY 5 +#define MEMX_VBLANK 6 +#define MEMX_TRAIN 7 + +/* I2C_: message identifiers */ +#define I2C__MSG_RD08 0 +#define I2C__MSG_WR08 1 + +#define I2C__MSG_DATA0_PORT 24:31 +#define I2C__MSG_DATA0_ADDR 14:23 + +#define I2C__MSG_DATA0_RD08_PORT I2C__MSG_DATA0_PORT +#define I2C__MSG_DATA0_RD08_ADDR I2C__MSG_DATA0_ADDR +#define I2C__MSG_DATA0_RD08_REG 0:7 +#define I2C__MSG_DATA1_RD08_VAL 0:7 + +#define I2C__MSG_DATA0_WR08_PORT I2C__MSG_DATA0_PORT +#define I2C__MSG_DATA0_WR08_ADDR I2C__MSG_DATA0_ADDR +#define I2C__MSG_DATA0_WR08_SYNC 8:8 +#define I2C__MSG_DATA0_WR08_REG 0:7 +#define I2C__MSG_DATA1_WR08_VAL 0:7 + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc new file mode 100644 index 000000000..38eadf705 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc @@ -0,0 +1,57 @@ +/* + * 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 + */ + +#ifdef INCLUDE_PROC +process(PROC_PERF, #perf_init, #perf_recv) +#endif + +/****************************************************************************** + * PERF data segment + *****************************************************************************/ +#ifdef INCLUDE_DATA +#endif + +/****************************************************************************** + * PERF code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE + +// description +// +// $r15 - current (perf) +// $r14 - sender process name +// $r13 - message +// $r12 - data0 +// $r11 - data1 +// $r0 - zero +perf_recv: + ret + +// description +// +// $r15 - current (perf) +// $r0 - zero +perf_init: + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc new file mode 100644 index 000000000..9e3f4e690 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc @@ -0,0 +1,63 @@ +/* + * 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 + */ + +#ifdef INCLUDE_PROC +process(PROC_TEST, #test_init, #test_recv) +#endif + +/****************************************************************************** + * TEST data segment + *****************************************************************************/ +#ifdef INCLUDE_DATA +#endif + +/****************************************************************************** + * TEST code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE +// description +// +// $r15 - current (test) +// $r14 - sender process name +// $r13 - message +// $r12 - data0 +// $r11 - data1 +// $r0 - zero +test_recv: + nv_iord($r1, NV_PPWR_DSCRATCH(2)) + add b32 $r1 1 + nv_iowr(NV_PPWR_DSCRATCH(2), $r1) + imm32($r14, 0x134fd900) + call(timer) + ret + +// description +// +// $r15 - current (test) +// $r0 - zero +test_init: + mov $r14 0x800 + call(timer) + ret +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c new file mode 100644 index 000000000..f725a3ec5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c @@ -0,0 +1,76 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "fuc/gf100.fuc3.h" + +#include + +void +gf100_pmu_reset(struct nvkm_pmu *pmu) +{ + struct nvkm_device *device = pmu->subdev.device; + nvkm_mc_disable(device, NVKM_SUBDEV_PMU, 0); + nvkm_mc_enable(device, NVKM_SUBDEV_PMU, 0); +} + +bool +gf100_pmu_enabled(struct nvkm_pmu *pmu) +{ + return nvkm_mc_enabled(pmu->subdev.device, NVKM_SUBDEV_PMU, 0); +} + +static const struct nvkm_pmu_func +gf100_pmu = { + .flcn = >215_pmu_flcn, + .code.data = gf100_pmu_code, + .code.size = sizeof(gf100_pmu_code), + .data.data = gf100_pmu_data, + .data.size = sizeof(gf100_pmu_data), + .enabled = gf100_pmu_enabled, + .reset = gf100_pmu_reset, + .init = gt215_pmu_init, + .fini = gt215_pmu_fini, + .intr = gt215_pmu_intr, + .send = gt215_pmu_send, + .recv = gt215_pmu_recv, +}; + +int +gf100_pmu_nofw(struct nvkm_pmu *pmu, int ver, const struct nvkm_pmu_fwif *fwif) +{ + return 0; +} + +static const struct nvkm_pmu_fwif +gf100_pmu_fwif[] = { + { -1, gf100_pmu_nofw, &gf100_pmu }, + {} +}; + +int +gf100_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gf100_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf119.c new file mode 100644 index 000000000..0f4b6697a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf119.c @@ -0,0 +1,54 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "fuc/gf119.fuc4.h" + +static const struct nvkm_pmu_func +gf119_pmu = { + .flcn = >215_pmu_flcn, + .code.data = gf119_pmu_code, + .code.size = sizeof(gf119_pmu_code), + .data.data = gf119_pmu_data, + .data.size = sizeof(gf119_pmu_data), + .enabled = gf100_pmu_enabled, + .reset = gf100_pmu_reset, + .init = gt215_pmu_init, + .fini = gt215_pmu_fini, + .intr = gt215_pmu_intr, + .send = gt215_pmu_send, + .recv = gt215_pmu_recv, +}; + +static const struct nvkm_pmu_fwif +gf119_pmu_fwif[] = { + { -1, gf100_pmu_nofw, &gf119_pmu }, + {} +}; + +int +gf119_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gf119_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c new file mode 100644 index 000000000..9e7631d7a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c @@ -0,0 +1,134 @@ +/* + * 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 + */ +#define gf119_pmu_code gk104_pmu_code +#define gf119_pmu_data gk104_pmu_data +#include "priv.h" +#include "fuc/gf119.fuc4.h" + +#include +#include +#include + +static void +magic_(struct nvkm_device *device, u32 ctrl, int size) +{ + nvkm_wr32(device, 0x00c800, 0x00000000); + nvkm_wr32(device, 0x00c808, 0x00000000); + nvkm_wr32(device, 0x00c800, ctrl); + nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x00c800) & 0x40000000) { + while (size--) + nvkm_wr32(device, 0x00c804, 0x00000000); + break; + } + ); + nvkm_wr32(device, 0x00c800, 0x00000000); +} + +static void +magic(struct nvkm_device *device, u32 ctrl) +{ + magic_(device, 0x8000a41f | ctrl, 6); + magic_(device, 0x80000421 | ctrl, 1); +} + +static void +gk104_pmu_pgob(struct nvkm_pmu *pmu, bool enable) +{ + struct nvkm_device *device = pmu->subdev.device; + + if (!(nvkm_fuse_read(device->fuse, 0x31c) & 0x00000001)) + return; + + nvkm_mask(device, 0x000200, 0x00001000, 0x00000000); + nvkm_rd32(device, 0x000200); + nvkm_mask(device, 0x000200, 0x08000000, 0x08000000); + msleep(50); + + nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000002); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000); + + nvkm_mask(device, 0x020004, 0xc0000000, enable ? 0xc0000000 : 0x40000000); + msleep(50); + + nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000000); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000); + + nvkm_mask(device, 0x000200, 0x08000000, 0x00000000); + nvkm_mask(device, 0x000200, 0x00001000, 0x00001000); + nvkm_rd32(device, 0x000200); + + if (nvkm_boolopt(device->cfgopt, "War00C800_0", true)) { + switch (device->chipset) { + case 0xe4: + magic(device, 0x04000000); + magic(device, 0x06000000); + magic(device, 0x0c000000); + magic(device, 0x0e000000); + break; + case 0xe6: + magic(device, 0x02000000); + magic(device, 0x04000000); + magic(device, 0x0a000000); + break; + case 0xe7: + magic(device, 0x02000000); + break; + default: + break; + } + } +} + +static const struct nvkm_pmu_func +gk104_pmu = { + .flcn = >215_pmu_flcn, + .code.data = gk104_pmu_code, + .code.size = sizeof(gk104_pmu_code), + .data.data = gk104_pmu_data, + .data.size = sizeof(gk104_pmu_data), + .enabled = gf100_pmu_enabled, + .reset = gf100_pmu_reset, + .init = gt215_pmu_init, + .fini = gt215_pmu_fini, + .intr = gt215_pmu_intr, + .send = gt215_pmu_send, + .recv = gt215_pmu_recv, + .pgob = gk104_pmu_pgob, +}; + +static const struct nvkm_pmu_fwif +gk104_pmu_fwif[] = { + { -1, gf100_pmu_nofw, &gk104_pmu }, + {} +}; + +int +gk104_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gk104_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c new file mode 100644 index 000000000..dbaefee53 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk110.c @@ -0,0 +1,113 @@ +/* + * Copyright 2015 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 + */ +#define gf119_pmu_code gk110_pmu_code +#define gf119_pmu_data gk110_pmu_data +#include "priv.h" +#include "fuc/gf119.fuc4.h" + +#include + +void +gk110_pmu_pgob(struct nvkm_pmu *pmu, bool enable) +{ + struct nvkm_device *device = pmu->subdev.device; + static const struct { + u32 addr; + u32 data; + } magic[] = { + { 0x020520, 0xfffffffc }, + { 0x020524, 0xfffffffe }, + { 0x020524, 0xfffffffc }, + { 0x020524, 0xfffffff8 }, + { 0x020524, 0xffffffe0 }, + { 0x020530, 0xfffffffe }, + { 0x02052c, 0xfffffffa }, + { 0x02052c, 0xfffffff0 }, + { 0x02052c, 0xffffffc0 }, + { 0x02052c, 0xffffff00 }, + { 0x02052c, 0xfffffc00 }, + { 0x02052c, 0xfffcfc00 }, + { 0x02052c, 0xfff0fc00 }, + { 0x02052c, 0xff80fc00 }, + { 0x020528, 0xfffffffe }, + { 0x020528, 0xfffffffc }, + }; + int i; + + nvkm_mask(device, 0x000200, 0x00001000, 0x00000000); + nvkm_rd32(device, 0x000200); + nvkm_mask(device, 0x000200, 0x08000000, 0x08000000); + msleep(50); + + nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000002); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000); + + nvkm_mask(device, 0x0206b4, 0x00000000, 0x00000000); + for (i = 0; i < ARRAY_SIZE(magic); i++) { + nvkm_wr32(device, magic[i].addr, magic[i].data); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, magic[i].addr) & 0x80000000)) + break; + ); + } + + nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000000); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001); + nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000); + + nvkm_mask(device, 0x000200, 0x08000000, 0x00000000); + nvkm_mask(device, 0x000200, 0x00001000, 0x00001000); + nvkm_rd32(device, 0x000200); +} + +static const struct nvkm_pmu_func +gk110_pmu = { + .flcn = >215_pmu_flcn, + .code.data = gk110_pmu_code, + .code.size = sizeof(gk110_pmu_code), + .data.data = gk110_pmu_data, + .data.size = sizeof(gk110_pmu_data), + .enabled = gf100_pmu_enabled, + .reset = gf100_pmu_reset, + .init = gt215_pmu_init, + .fini = gt215_pmu_fini, + .intr = gt215_pmu_intr, + .send = gt215_pmu_send, + .recv = gt215_pmu_recv, + .pgob = gk110_pmu_pgob, +}; + +static const struct nvkm_pmu_fwif +gk110_pmu_fwif[] = { + { -1, gf100_pmu_nofw, &gk110_pmu }, + {} +}; + +int +gk110_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gk110_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c new file mode 100644 index 000000000..a08fb049e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "fuc/gk208.fuc5.h" + +static const struct nvkm_pmu_func +gk208_pmu = { + .flcn = >215_pmu_flcn, + .code.data = gk208_pmu_code, + .code.size = sizeof(gk208_pmu_code), + .data.data = gk208_pmu_data, + .data.size = sizeof(gk208_pmu_data), + .enabled = gf100_pmu_enabled, + .reset = gf100_pmu_reset, + .init = gt215_pmu_init, + .fini = gt215_pmu_fini, + .intr = gt215_pmu_intr, + .send = gt215_pmu_send, + .recv = gt215_pmu_recv, + .pgob = gk110_pmu_pgob, +}; + +static const struct nvkm_pmu_fwif +gk208_pmu_fwif[] = { + { -1, gf100_pmu_nofw, &gk208_pmu }, + {} +}; + +int +gk208_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gk208_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c new file mode 100644 index 000000000..a67a42e73 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define gk20a_pmu(p) container_of((p), struct gk20a_pmu, base) +#include "priv.h" + +#include +#include +#include + +#define BUSY_SLOT 0 +#define CLK_SLOT 7 + +struct gk20a_pmu_dvfs_data { + int p_load_target; + int p_load_max; + int p_smooth; + unsigned int avg_load; +}; + +struct gk20a_pmu { + struct nvkm_pmu base; + struct nvkm_alarm alarm; + struct gk20a_pmu_dvfs_data *data; +}; + +struct gk20a_pmu_dvfs_dev_status { + u32 total; + u32 busy; +}; + +static int +gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state) +{ + struct nvkm_clk *clk = pmu->base.subdev.device->clk; + + return nvkm_clk_astate(clk, *state, 0, false); +} + +static void +gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state) +{ + struct nvkm_clk *clk = pmu->base.subdev.device->clk; + + *state = clk->pstate; +} + +static int +gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu *pmu, + int *state, int load) +{ + struct gk20a_pmu_dvfs_data *data = pmu->data; + struct nvkm_clk *clk = pmu->base.subdev.device->clk; + int cur_level, level; + + /* For GK20A, the performance level is directly mapped to pstate */ + level = cur_level = clk->pstate; + + if (load > data->p_load_max) { + level = min(clk->state_nr - 1, level + (clk->state_nr / 3)); + } else { + level += ((load - data->p_load_target) * 10 / + data->p_load_target) / 2; + level = max(0, level); + level = min(clk->state_nr - 1, level); + } + + nvkm_trace(&pmu->base.subdev, "cur level = %d, new level = %d\n", + cur_level, level); + + *state = level; + + return (level != cur_level); +} + +static void +gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu *pmu, + struct gk20a_pmu_dvfs_dev_status *status) +{ + struct nvkm_falcon *falcon = &pmu->base.falcon; + + status->busy = nvkm_falcon_rd32(falcon, 0x508 + (BUSY_SLOT * 0x10)); + status->total= nvkm_falcon_rd32(falcon, 0x508 + (CLK_SLOT * 0x10)); +} + +static void +gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu *pmu) +{ + struct nvkm_falcon *falcon = &pmu->base.falcon; + + nvkm_falcon_wr32(falcon, 0x508 + (BUSY_SLOT * 0x10), 0x80000000); + nvkm_falcon_wr32(falcon, 0x508 + (CLK_SLOT * 0x10), 0x80000000); +} + +static void +gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm) +{ + struct gk20a_pmu *pmu = + container_of(alarm, struct gk20a_pmu, alarm); + struct gk20a_pmu_dvfs_data *data = pmu->data; + struct gk20a_pmu_dvfs_dev_status status; + struct nvkm_subdev *subdev = &pmu->base.subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_clk *clk = device->clk; + struct nvkm_timer *tmr = device->timer; + struct nvkm_volt *volt = device->volt; + u32 utilization = 0; + int state; + + /* + * The PMU is initialized before CLK and VOLT, so we have to make sure the + * CLK and VOLT are ready here. + */ + if (!clk || !volt) + goto resched; + + gk20a_pmu_dvfs_get_dev_status(pmu, &status); + + if (status.total) + utilization = div_u64((u64)status.busy * 100, status.total); + + data->avg_load = (data->p_smooth * data->avg_load) + utilization; + data->avg_load /= data->p_smooth + 1; + nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n", + utilization, data->avg_load); + + gk20a_pmu_dvfs_get_cur_state(pmu, &state); + + if (gk20a_pmu_dvfs_get_target_state(pmu, &state, data->avg_load)) { + nvkm_trace(subdev, "set new state to %d\n", state); + gk20a_pmu_dvfs_target(pmu, &state); + } + +resched: + gk20a_pmu_dvfs_reset_dev_status(pmu); + nvkm_timer_alarm(tmr, 100000000, alarm); +} + +static void +gk20a_pmu_fini(struct nvkm_pmu *pmu) +{ + struct gk20a_pmu *gpmu = gk20a_pmu(pmu); + nvkm_timer_alarm(pmu->subdev.device->timer, 0, &gpmu->alarm); + + nvkm_falcon_put(&pmu->falcon, &pmu->subdev); +} + +static int +gk20a_pmu_init(struct nvkm_pmu *pmu) +{ + struct gk20a_pmu *gpmu = gk20a_pmu(pmu); + struct nvkm_subdev *subdev = &pmu->subdev; + struct nvkm_device *device = pmu->subdev.device; + struct nvkm_falcon *falcon = &pmu->falcon; + int ret; + + ret = nvkm_falcon_get(falcon, subdev); + if (ret) { + nvkm_error(subdev, "cannot acquire %s falcon!\n", falcon->name); + return ret; + } + + /* init pwr perf counter */ + nvkm_falcon_wr32(falcon, 0x504 + (BUSY_SLOT * 0x10), 0x00200001); + nvkm_falcon_wr32(falcon, 0x50c + (BUSY_SLOT * 0x10), 0x00000002); + nvkm_falcon_wr32(falcon, 0x50c + (CLK_SLOT * 0x10), 0x00000003); + + nvkm_timer_alarm(device->timer, 2000000000, &gpmu->alarm); + return 0; +} + +static struct gk20a_pmu_dvfs_data +gk20a_dvfs_data= { + .p_load_target = 70, + .p_load_max = 90, + .p_smooth = 1, +}; + +static const struct nvkm_pmu_func +gk20a_pmu = { + .flcn = >215_pmu_flcn, + .enabled = gf100_pmu_enabled, + .init = gk20a_pmu_init, + .fini = gk20a_pmu_fini, + .reset = gf100_pmu_reset, +}; + +static const struct nvkm_pmu_fwif +gk20a_pmu_fwif[] = { + { -1, gf100_pmu_nofw, &gk20a_pmu }, + {} +}; + +int +gk20a_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + struct gk20a_pmu *pmu; + int ret; + + if (!(pmu = kzalloc(sizeof(*pmu), GFP_KERNEL))) + return -ENOMEM; + *ppmu = &pmu->base; + + ret = nvkm_pmu_ctor(gk20a_pmu_fwif, device, type, inst, &pmu->base); + if (ret) + return ret; + + pmu->data = &gk20a_dvfs_data; + nvkm_alarm_init(&pmu->alarm, gk20a_pmu_dvfs_work); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm107.c new file mode 100644 index 000000000..622ee637f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm107.c @@ -0,0 +1,56 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#define gk208_pmu_code gm107_pmu_code +#define gk208_pmu_data gm107_pmu_data +#include "fuc/gk208.fuc5.h" + +static const struct nvkm_pmu_func +gm107_pmu = { + .flcn = >215_pmu_flcn, + .code.data = gm107_pmu_code, + .code.size = sizeof(gm107_pmu_code), + .data.data = gm107_pmu_data, + .data.size = sizeof(gm107_pmu_data), + .enabled = gf100_pmu_enabled, + .reset = gf100_pmu_reset, + .init = gt215_pmu_init, + .fini = gt215_pmu_fini, + .intr = gt215_pmu_intr, + .send = gt215_pmu_send, + .recv = gt215_pmu_recv, +}; + +static const struct nvkm_pmu_fwif +gm107_pmu_fwif[] = { + { -1, gf100_pmu_nofw, &gm107_pmu }, + {} +}; + +int +gm107_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gm107_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm200.c new file mode 100644 index 000000000..40439e329 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm200.c @@ -0,0 +1,81 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static int +gm200_pmu_flcn_reset(struct nvkm_falcon *falcon) +{ + struct nvkm_pmu *pmu = container_of(falcon, typeof(*pmu), falcon); + + nvkm_falcon_wr32(falcon, 0x014, 0x0000ffff); + pmu->func->reset(pmu); + return nvkm_falcon_enable(falcon); +} + +const struct nvkm_falcon_func +gm200_pmu_flcn = { + .debug = 0xc08, + .fbif = 0xe00, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .bind_context = nvkm_falcon_v1_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = nvkm_falcon_v1_enable, + .disable = nvkm_falcon_v1_disable, + .reset = gm200_pmu_flcn_reset, + .cmdq = { 0x4a0, 0x4b0, 4 }, + .msgq = { 0x4c8, 0x4cc, 0 }, +}; + +static const struct nvkm_pmu_func +gm200_pmu = { + .flcn = &gm200_pmu_flcn, + .enabled = gf100_pmu_enabled, + .reset = gf100_pmu_reset, +}; + + +int +gm200_pmu_nofw(struct nvkm_pmu *pmu, int ver, const struct nvkm_pmu_fwif *fwif) +{ + nvkm_warn(&pmu->subdev, "firmware unavailable\n"); + return 0; +} + +static const struct nvkm_pmu_fwif +gm200_pmu_fwif[] = { + { -1, gm200_pmu_nofw, &gm200_pmu }, + {} +}; + +int +gm200_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gm200_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c new file mode 100644 index 000000000..612310d5d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include +#include + +#include +#include + +static int +gm20b_pmu_acr_bootstrap_falcon_cb(void *priv, struct nvfw_falcon_msg *hdr) +{ + struct nv_pmu_acr_bootstrap_falcon_msg *msg = + container_of(hdr, typeof(*msg), msg.hdr); + return msg->falcon_id; +} + +int +gm20b_pmu_acr_bootstrap_falcon(struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id) +{ + struct nvkm_pmu *pmu = container_of(falcon, typeof(*pmu), falcon); + struct nv_pmu_acr_bootstrap_falcon_cmd cmd = { + .cmd.hdr.unit_id = NV_PMU_UNIT_ACR, + .cmd.hdr.size = sizeof(cmd), + .cmd.cmd_type = NV_PMU_ACR_CMD_BOOTSTRAP_FALCON, + .flags = NV_PMU_ACR_BOOTSTRAP_FALCON_FLAGS_RESET_YES, + .falcon_id = id, + }; + int ret; + + ret = nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr, + gm20b_pmu_acr_bootstrap_falcon_cb, + &pmu->subdev, msecs_to_jiffies(1000)); + if (ret >= 0) { + if (ret != cmd.falcon_id) + ret = -EIO; + else + ret = 0; + } + + return ret; +} + +int +gm20b_pmu_acr_boot(struct nvkm_falcon *falcon) +{ + struct nv_pmu_args args = { .secure_mode = true }; + const u32 addr_args = falcon->data.limit - sizeof(struct nv_pmu_args); + nvkm_falcon_load_dmem(falcon, &args, addr_args, sizeof(args), 0); + nvkm_falcon_start(falcon); + return 0; +} + +void +gm20b_pmu_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust) +{ + struct loader_config hdr; + u64 addr; + + nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr)); + addr = ((u64)hdr.code_dma_base1 << 40 | hdr.code_dma_base << 8); + hdr.code_dma_base = lower_32_bits((addr + adjust) >> 8); + hdr.code_dma_base1 = upper_32_bits((addr + adjust) >> 8); + addr = ((u64)hdr.data_dma_base1 << 40 | hdr.data_dma_base << 8); + hdr.data_dma_base = lower_32_bits((addr + adjust) >> 8); + hdr.data_dma_base1 = upper_32_bits((addr + adjust) >> 8); + addr = ((u64)hdr.overlay_dma_base1 << 40 | hdr.overlay_dma_base << 8); + hdr.overlay_dma_base = lower_32_bits((addr + adjust) << 8); + hdr.overlay_dma_base1 = upper_32_bits((addr + adjust) << 8); + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); + + loader_config_dump(&acr->subdev, &hdr); +} + +void +gm20b_pmu_acr_bld_write(struct nvkm_acr *acr, u32 bld, + struct nvkm_acr_lsfw *lsfw) +{ + const u64 base = lsfw->offset.img + lsfw->app_start_offset; + const u64 code = (base + lsfw->app_resident_code_offset) >> 8; + const u64 data = (base + lsfw->app_resident_data_offset) >> 8; + const struct loader_config hdr = { + .dma_idx = FALCON_DMAIDX_UCODE, + .code_dma_base = lower_32_bits(code), + .code_size_total = lsfw->app_size, + .code_size_to_load = lsfw->app_resident_code_size, + .code_entry_point = lsfw->app_imem_entry, + .data_dma_base = lower_32_bits(data), + .data_size = lsfw->app_resident_data_size, + .overlay_dma_base = lower_32_bits(code), + .argc = 1, + .argv = lsfw->falcon->data.limit - sizeof(struct nv_pmu_args), + .code_dma_base1 = upper_32_bits(code), + .data_dma_base1 = upper_32_bits(data), + .overlay_dma_base1 = upper_32_bits(code), + }; + + nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr)); +} + +static const struct nvkm_acr_lsf_func +gm20b_pmu_acr = { + .flags = NVKM_ACR_LSF_DMACTL_REQ_CTX, + .bld_size = sizeof(struct loader_config), + .bld_write = gm20b_pmu_acr_bld_write, + .bld_patch = gm20b_pmu_acr_bld_patch, + .boot = gm20b_pmu_acr_boot, + .bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_PMU) | + BIT_ULL(NVKM_ACR_LSF_FECS) | + BIT_ULL(NVKM_ACR_LSF_GPCCS), + .bootstrap_falcon = gm20b_pmu_acr_bootstrap_falcon, +}; + +static int +gm20b_pmu_acr_init_wpr_callback(void *priv, struct nvfw_falcon_msg *hdr) +{ + struct nv_pmu_acr_init_wpr_region_msg *msg = + container_of(hdr, typeof(*msg), msg.hdr); + struct nvkm_pmu *pmu = priv; + struct nvkm_subdev *subdev = &pmu->subdev; + + if (msg->error_code) { + nvkm_error(subdev, "ACR WPR init failure: %d\n", + msg->error_code); + return -EINVAL; + } + + nvkm_debug(subdev, "ACR WPR init complete\n"); + complete_all(&pmu->wpr_ready); + return 0; +} + +static int +gm20b_pmu_acr_init_wpr(struct nvkm_pmu *pmu) +{ + struct nv_pmu_acr_init_wpr_region_cmd cmd = { + .cmd.hdr.unit_id = NV_PMU_UNIT_ACR, + .cmd.hdr.size = sizeof(cmd), + .cmd.cmd_type = NV_PMU_ACR_CMD_INIT_WPR_REGION, + .region_id = 1, + .wpr_offset = 0, + }; + + return nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr, + gm20b_pmu_acr_init_wpr_callback, pmu, 0); +} + +int +gm20b_pmu_initmsg(struct nvkm_pmu *pmu) +{ + struct nv_pmu_init_msg msg; + int ret; + + ret = nvkm_falcon_msgq_recv_initmsg(pmu->msgq, &msg, sizeof(msg)); + if (ret) + return ret; + + if (msg.hdr.unit_id != NV_PMU_UNIT_INIT || + msg.msg_type != NV_PMU_INIT_MSG_INIT) + return -EINVAL; + + nvkm_falcon_cmdq_init(pmu->hpq, msg.queue_info[0].index, + msg.queue_info[0].offset, + msg.queue_info[0].size); + nvkm_falcon_cmdq_init(pmu->lpq, msg.queue_info[1].index, + msg.queue_info[1].offset, + msg.queue_info[1].size); + nvkm_falcon_msgq_init(pmu->msgq, msg.queue_info[4].index, + msg.queue_info[4].offset, + msg.queue_info[4].size); + return gm20b_pmu_acr_init_wpr(pmu); +} + +void +gm20b_pmu_recv(struct nvkm_pmu *pmu) +{ + if (!pmu->initmsg_received) { + int ret = pmu->func->initmsg(pmu); + if (ret) { + nvkm_error(&pmu->subdev, + "error parsing init message: %d\n", ret); + return; + } + + pmu->initmsg_received = true; + } + + nvkm_falcon_msgq_recv(pmu->msgq); +} + +static const struct nvkm_pmu_func +gm20b_pmu = { + .flcn = &gm200_pmu_flcn, + .enabled = gf100_pmu_enabled, + .intr = gt215_pmu_intr, + .recv = gm20b_pmu_recv, + .initmsg = gm20b_pmu_initmsg, + .reset = gf100_pmu_reset, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +MODULE_FIRMWARE("nvidia/gm20b/pmu/desc.bin"); +MODULE_FIRMWARE("nvidia/gm20b/pmu/image.bin"); +MODULE_FIRMWARE("nvidia/gm20b/pmu/sig.bin"); +#endif + +int +gm20b_pmu_load(struct nvkm_pmu *pmu, int ver, const struct nvkm_pmu_fwif *fwif) +{ + return nvkm_acr_lsfw_load_sig_image_desc(&pmu->subdev, &pmu->falcon, + NVKM_ACR_LSF_PMU, "pmu/", + ver, fwif->acr); +} + +static const struct nvkm_pmu_fwif +gm20b_pmu_fwif[] = { + { 0, gm20b_pmu_load, &gm20b_pmu, &gm20b_pmu_acr }, + { -1, gm200_pmu_nofw, &gm20b_pmu }, + {} +}; + +int +gm20b_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gm20b_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp102.c new file mode 100644 index 000000000..1a6f9c3af --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp102.c @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +void +gp102_pmu_reset(struct nvkm_pmu *pmu) +{ + struct nvkm_device *device = pmu->subdev.device; + nvkm_mask(device, 0x10a3c0, 0x00000001, 0x00000001); + nvkm_mask(device, 0x10a3c0, 0x00000001, 0x00000000); +} + +static bool +gp102_pmu_enabled(struct nvkm_pmu *pmu) +{ + return !(nvkm_rd32(pmu->subdev.device, 0x10a3c0) & 0x00000001); +} + +static const struct nvkm_pmu_func +gp102_pmu = { + .flcn = &gm200_pmu_flcn, + .enabled = gp102_pmu_enabled, + .reset = gp102_pmu_reset, +}; + +static const struct nvkm_pmu_fwif +gp102_pmu_fwif[] = { + { -1, gm200_pmu_nofw, &gp102_pmu }, + {} +}; + +int +gp102_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gp102_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp10b.c new file mode 100644 index 000000000..94cfb1791 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp10b.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include + +#include +#include + +static int +gp10b_pmu_acr_bootstrap_multiple_falcons_cb(void *priv, + struct nvfw_falcon_msg *hdr) +{ + struct nv_pmu_acr_bootstrap_multiple_falcons_msg *msg = + container_of(hdr, typeof(*msg), msg.hdr); + return msg->falcon_mask; +} +static int +gp10b_pmu_acr_bootstrap_multiple_falcons(struct nvkm_falcon *falcon, u32 mask) +{ + struct nvkm_pmu *pmu = container_of(falcon, typeof(*pmu), falcon); + struct nv_pmu_acr_bootstrap_multiple_falcons_cmd cmd = { + .cmd.hdr.unit_id = NV_PMU_UNIT_ACR, + .cmd.hdr.size = sizeof(cmd), + .cmd.cmd_type = NV_PMU_ACR_CMD_BOOTSTRAP_MULTIPLE_FALCONS, + .flags = NV_PMU_ACR_BOOTSTRAP_MULTIPLE_FALCONS_FLAGS_RESET_YES, + .falcon_mask = mask, + .wpr_lo = 0, /*XXX*/ + .wpr_hi = 0, /*XXX*/ + }; + int ret; + + ret = nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr, + gp10b_pmu_acr_bootstrap_multiple_falcons_cb, + &pmu->subdev, msecs_to_jiffies(1000)); + if (ret >= 0) { + if (ret != cmd.falcon_mask) + ret = -EIO; + else + ret = 0; + } + + return ret; +} + +static const struct nvkm_acr_lsf_func +gp10b_pmu_acr = { + .flags = NVKM_ACR_LSF_DMACTL_REQ_CTX, + .bld_size = sizeof(struct loader_config), + .bld_write = gm20b_pmu_acr_bld_write, + .bld_patch = gm20b_pmu_acr_bld_patch, + .boot = gm20b_pmu_acr_boot, + .bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_PMU) | + BIT_ULL(NVKM_ACR_LSF_FECS) | + BIT_ULL(NVKM_ACR_LSF_GPCCS), + .bootstrap_falcon = gm20b_pmu_acr_bootstrap_falcon, + .bootstrap_multiple_falcons = gp10b_pmu_acr_bootstrap_multiple_falcons, +}; + +static const struct nvkm_pmu_func +gp10b_pmu = { + .flcn = &gm200_pmu_flcn, + .enabled = gf100_pmu_enabled, + .intr = gt215_pmu_intr, + .recv = gm20b_pmu_recv, + .initmsg = gm20b_pmu_initmsg, + .reset = gp102_pmu_reset, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +MODULE_FIRMWARE("nvidia/gp10b/pmu/desc.bin"); +MODULE_FIRMWARE("nvidia/gp10b/pmu/image.bin"); +MODULE_FIRMWARE("nvidia/gp10b/pmu/sig.bin"); +#endif + +static const struct nvkm_pmu_fwif +gp10b_pmu_fwif[] = { + { 0, gm20b_pmu_load, &gp10b_pmu, &gp10b_pmu_acr }, + { -1, gm200_pmu_nofw, &gp10b_pmu }, + {} +}; + +int +gp10b_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gp10b_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c new file mode 100644 index 000000000..b0407b86b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c @@ -0,0 +1,289 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "fuc/gt215.fuc3.h" + +#include + +int +gt215_pmu_send(struct nvkm_pmu *pmu, u32 reply[2], + u32 process, u32 message, u32 data0, u32 data1) +{ + struct nvkm_subdev *subdev = &pmu->subdev; + struct nvkm_device *device = subdev->device; + u32 addr; + + mutex_lock(&pmu->send.mutex); + /* wait for a free slot in the fifo */ + addr = nvkm_rd32(device, 0x10a4a0); + if (nvkm_msec(device, 2000, + u32 tmp = nvkm_rd32(device, 0x10a4b0); + if (tmp != (addr ^ 8)) + break; + ) < 0) { + mutex_unlock(&pmu->send.mutex); + return -EBUSY; + } + + /* we currently only support a single process at a time waiting + * on a synchronous reply, take the PMU mutex and tell the + * receive handler what we're waiting for + */ + if (reply) { + pmu->recv.message = message; + pmu->recv.process = process; + } + + /* acquire data segment access */ + do { + nvkm_wr32(device, 0x10a580, 0x00000001); + } while (nvkm_rd32(device, 0x10a580) != 0x00000001); + + /* write the packet */ + nvkm_wr32(device, 0x10a1c0, 0x01000000 | (((addr & 0x07) << 4) + + pmu->send.base)); + nvkm_wr32(device, 0x10a1c4, process); + nvkm_wr32(device, 0x10a1c4, message); + nvkm_wr32(device, 0x10a1c4, data0); + nvkm_wr32(device, 0x10a1c4, data1); + nvkm_wr32(device, 0x10a4a0, (addr + 1) & 0x0f); + + /* release data segment access */ + nvkm_wr32(device, 0x10a580, 0x00000000); + + /* wait for reply, if requested */ + if (reply) { + wait_event(pmu->recv.wait, (pmu->recv.process == 0)); + reply[0] = pmu->recv.data[0]; + reply[1] = pmu->recv.data[1]; + } + + mutex_unlock(&pmu->send.mutex); + return 0; +} + +void +gt215_pmu_recv(struct nvkm_pmu *pmu) +{ + struct nvkm_subdev *subdev = &pmu->subdev; + struct nvkm_device *device = subdev->device; + u32 process, message, data0, data1; + + /* nothing to do if GET == PUT */ + u32 addr = nvkm_rd32(device, 0x10a4cc); + if (addr == nvkm_rd32(device, 0x10a4c8)) + return; + + /* acquire data segment access */ + do { + nvkm_wr32(device, 0x10a580, 0x00000002); + } while (nvkm_rd32(device, 0x10a580) != 0x00000002); + + /* read the packet */ + nvkm_wr32(device, 0x10a1c0, 0x02000000 | (((addr & 0x07) << 4) + + pmu->recv.base)); + process = nvkm_rd32(device, 0x10a1c4); + message = nvkm_rd32(device, 0x10a1c4); + data0 = nvkm_rd32(device, 0x10a1c4); + data1 = nvkm_rd32(device, 0x10a1c4); + nvkm_wr32(device, 0x10a4cc, (addr + 1) & 0x0f); + + /* release data segment access */ + nvkm_wr32(device, 0x10a580, 0x00000000); + + /* wake process if it's waiting on a synchronous reply */ + if (pmu->recv.process) { + if (process == pmu->recv.process && + message == pmu->recv.message) { + pmu->recv.data[0] = data0; + pmu->recv.data[1] = data1; + pmu->recv.process = 0; + wake_up(&pmu->recv.wait); + return; + } + } + + /* right now there's no other expected responses from the engine, + * so assume that any unexpected message is an error. + */ + nvkm_warn(subdev, "%c%c%c%c %08x %08x %08x %08x\n", + (char)((process & 0x000000ff) >> 0), + (char)((process & 0x0000ff00) >> 8), + (char)((process & 0x00ff0000) >> 16), + (char)((process & 0xff000000) >> 24), + process, message, data0, data1); +} + +void +gt215_pmu_intr(struct nvkm_pmu *pmu) +{ + struct nvkm_subdev *subdev = &pmu->subdev; + struct nvkm_device *device = subdev->device; + u32 disp = nvkm_rd32(device, 0x10a01c); + u32 intr = nvkm_rd32(device, 0x10a008) & disp & ~(disp >> 16); + + if (intr & 0x00000020) { + u32 stat = nvkm_rd32(device, 0x10a16c); + if (stat & 0x80000000) { + nvkm_error(subdev, "UAS fault at %06x addr %08x\n", + stat & 0x00ffffff, + nvkm_rd32(device, 0x10a168)); + nvkm_wr32(device, 0x10a16c, 0x00000000); + intr &= ~0x00000020; + } + } + + if (intr & 0x00000040) { + schedule_work(&pmu->recv.work); + nvkm_wr32(device, 0x10a004, 0x00000040); + intr &= ~0x00000040; + } + + if (intr & 0x00000080) { + nvkm_info(subdev, "wr32 %06x %08x\n", + nvkm_rd32(device, 0x10a7a0), + nvkm_rd32(device, 0x10a7a4)); + nvkm_wr32(device, 0x10a004, 0x00000080); + intr &= ~0x00000080; + } + + if (intr) { + nvkm_error(subdev, "intr %08x\n", intr); + nvkm_wr32(device, 0x10a004, intr); + } +} + +void +gt215_pmu_fini(struct nvkm_pmu *pmu) +{ + nvkm_wr32(pmu->subdev.device, 0x10a014, 0x00000060); +} + +static void +gt215_pmu_reset(struct nvkm_pmu *pmu) +{ + struct nvkm_device *device = pmu->subdev.device; + nvkm_mask(device, 0x022210, 0x00000001, 0x00000000); + nvkm_mask(device, 0x022210, 0x00000001, 0x00000001); + nvkm_rd32(device, 0x022210); +} + +static bool +gt215_pmu_enabled(struct nvkm_pmu *pmu) +{ + return nvkm_rd32(pmu->subdev.device, 0x022210) & 0x00000001; +} + +int +gt215_pmu_init(struct nvkm_pmu *pmu) +{ + struct nvkm_device *device = pmu->subdev.device; + int i; + + /* upload data segment */ + nvkm_wr32(device, 0x10a1c0, 0x01000000); + for (i = 0; i < pmu->func->data.size / 4; i++) + nvkm_wr32(device, 0x10a1c4, pmu->func->data.data[i]); + + /* upload code segment */ + nvkm_wr32(device, 0x10a180, 0x01000000); + for (i = 0; i < pmu->func->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nvkm_wr32(device, 0x10a188, i >> 6); + nvkm_wr32(device, 0x10a184, pmu->func->code.data[i]); + } + + /* start it running */ + nvkm_wr32(device, 0x10a10c, 0x00000000); + nvkm_wr32(device, 0x10a104, 0x00000000); + nvkm_wr32(device, 0x10a100, 0x00000002); + + /* wait for valid host->pmu ring configuration */ + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x10a4d0)) + break; + ) < 0) + return -EBUSY; + pmu->send.base = nvkm_rd32(device, 0x10a4d0) & 0x0000ffff; + pmu->send.size = nvkm_rd32(device, 0x10a4d0) >> 16; + + /* wait for valid pmu->host ring configuration */ + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x10a4dc)) + break; + ) < 0) + return -EBUSY; + pmu->recv.base = nvkm_rd32(device, 0x10a4dc) & 0x0000ffff; + pmu->recv.size = nvkm_rd32(device, 0x10a4dc) >> 16; + + nvkm_wr32(device, 0x10a010, 0x000000e0); + return 0; +} + +const struct nvkm_falcon_func +gt215_pmu_flcn = { + .debug = 0xc08, + .fbif = 0xe00, + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .bind_context = nvkm_falcon_v1_bind_context, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .set_start_addr = nvkm_falcon_v1_set_start_addr, + .start = nvkm_falcon_v1_start, + .enable = nvkm_falcon_v1_enable, + .disable = nvkm_falcon_v1_disable, + .cmdq = { 0x4a0, 0x4b0, 4 }, + .msgq = { 0x4c8, 0x4cc, 0 }, +}; + +static const struct nvkm_pmu_func +gt215_pmu = { + .flcn = >215_pmu_flcn, + .code.data = gt215_pmu_code, + .code.size = sizeof(gt215_pmu_code), + .data.data = gt215_pmu_data, + .data.size = sizeof(gt215_pmu_data), + .enabled = gt215_pmu_enabled, + .reset = gt215_pmu_reset, + .init = gt215_pmu_init, + .fini = gt215_pmu_fini, + .intr = gt215_pmu_intr, + .send = gt215_pmu_send, + .recv = gt215_pmu_recv, +}; + +static const struct nvkm_pmu_fwif +gt215_pmu_fwif[] = { + { -1, gf100_pmu_nofw, >215_pmu }, + {} +}; + +int +gt215_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(gt215_pmu_fwif, device, type, inst, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c new file mode 100644 index 000000000..22eaebefc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: MIT +#ifndef __NVKM_PMU_MEMX_H__ +#define __NVKM_PMU_MEMX_H__ +#include "priv.h" + +struct nvkm_memx { + struct nvkm_pmu *pmu; + u32 base; + u32 size; + struct { + u32 mthd; + u32 size; + u32 data[64]; + } c; +}; + +static void +memx_out(struct nvkm_memx *memx) +{ + struct nvkm_device *device = memx->pmu->subdev.device; + int i; + + if (memx->c.mthd) { + nvkm_wr32(device, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd); + for (i = 0; i < memx->c.size; i++) + nvkm_wr32(device, 0x10a1c4, memx->c.data[i]); + memx->c.mthd = 0; + memx->c.size = 0; + } +} + +static void +memx_cmd(struct nvkm_memx *memx, u32 mthd, u32 size, u32 data[]) +{ + if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) || + (memx->c.mthd && memx->c.mthd != mthd)) + memx_out(memx); + memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0])); + memx->c.size += size; + memx->c.mthd = mthd; +} + +int +nvkm_memx_init(struct nvkm_pmu *pmu, struct nvkm_memx **pmemx) +{ + struct nvkm_device *device = pmu->subdev.device; + struct nvkm_memx *memx; + u32 reply[2]; + int ret; + + ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO, + MEMX_INFO_DATA, 0); + if (ret) + return ret; + + memx = *pmemx = kzalloc(sizeof(*memx), GFP_KERNEL); + if (!memx) + return -ENOMEM; + memx->pmu = pmu; + memx->base = reply[0]; + memx->size = reply[1]; + + /* acquire data segment access */ + do { + nvkm_wr32(device, 0x10a580, 0x00000003); + } while (nvkm_rd32(device, 0x10a580) != 0x00000003); + nvkm_wr32(device, 0x10a1c0, 0x01000000 | memx->base); + return 0; +} + +int +nvkm_memx_fini(struct nvkm_memx **pmemx, bool exec) +{ + struct nvkm_memx *memx = *pmemx; + struct nvkm_pmu *pmu = memx->pmu; + struct nvkm_subdev *subdev = &pmu->subdev; + struct nvkm_device *device = subdev->device; + u32 finish, reply[2]; + + /* flush the cache... */ + memx_out(memx); + + /* release data segment access */ + finish = nvkm_rd32(device, 0x10a1c0) & 0x00ffffff; + nvkm_wr32(device, 0x10a580, 0x00000000); + + /* call MEMX process to execute the script, and wait for reply */ + if (exec) { + nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_EXEC, + memx->base, finish); + nvkm_debug(subdev, "Exec took %uns, PMU_IN %08x\n", + reply[0], reply[1]); + } + + kfree(memx); + return 0; +} + +void +nvkm_memx_wr32(struct nvkm_memx *memx, u32 addr, u32 data) +{ + nvkm_debug(&memx->pmu->subdev, "R[%06x] = %08x\n", addr, data); + memx_cmd(memx, MEMX_WR32, 2, (u32[]){ addr, data }); +} + +void +nvkm_memx_wait(struct nvkm_memx *memx, + u32 addr, u32 mask, u32 data, u32 nsec) +{ + nvkm_debug(&memx->pmu->subdev, "R[%06x] & %08x == %08x, %d us\n", + addr, mask, data, nsec); + memx_cmd(memx, MEMX_WAIT, 4, (u32[]){ addr, mask, data, nsec }); + memx_out(memx); /* fuc can't handle multiple */ +} + +void +nvkm_memx_nsec(struct nvkm_memx *memx, u32 nsec) +{ + nvkm_debug(&memx->pmu->subdev, " DELAY = %d ns\n", nsec); + memx_cmd(memx, MEMX_DELAY, 1, (u32[]){ nsec }); + memx_out(memx); /* fuc can't handle multiple */ +} + +void +nvkm_memx_wait_vblank(struct nvkm_memx *memx) +{ + struct nvkm_subdev *subdev = &memx->pmu->subdev; + struct nvkm_device *device = subdev->device; + u32 heads, x, y, px = 0; + int i, head_sync; + + if (device->chipset < 0xd0) { + heads = nvkm_rd32(device, 0x610050); + for (i = 0; i < 2; i++) { + /* Heuristic: sync to head with biggest resolution */ + if (heads & (2 << (i << 3))) { + x = nvkm_rd32(device, 0x610b40 + (0x540 * i)); + y = (x & 0xffff0000) >> 16; + x &= 0x0000ffff; + if ((x * y) > px) { + px = (x * y); + head_sync = i; + } + } + } + } + + if (px == 0) { + nvkm_debug(subdev, "WAIT VBLANK !NO ACTIVE HEAD\n"); + return; + } + + nvkm_debug(subdev, "WAIT VBLANK HEAD%d\n", head_sync); + memx_cmd(memx, MEMX_VBLANK, 1, (u32[]){ head_sync }); + memx_out(memx); /* fuc can't handle multiple */ +} + +void +nvkm_memx_train(struct nvkm_memx *memx) +{ + nvkm_debug(&memx->pmu->subdev, " MEM TRAIN\n"); + memx_cmd(memx, MEMX_TRAIN, 0, NULL); +} + +int +nvkm_memx_train_result(struct nvkm_pmu *pmu, u32 *res, int rsize) +{ + struct nvkm_device *device = pmu->subdev.device; + u32 reply[2], base, size, i; + int ret; + + ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO, + MEMX_INFO_TRAIN, 0); + if (ret) + return ret; + + base = reply[0]; + size = reply[1] >> 2; + if (size > rsize) + return -ENOMEM; + + /* read the packet */ + nvkm_wr32(device, 0x10a1c0, 0x02000000 | base); + + for (i = 0; i < size; i++) + res[i] = nvkm_rd32(device, 0x10a1c4); + + return 0; +} + +void +nvkm_memx_block(struct nvkm_memx *memx) +{ + nvkm_debug(&memx->pmu->subdev, " HOST BLOCKED\n"); + memx_cmd(memx, MEMX_ENTER, 0, NULL); +} + +void +nvkm_memx_unblock(struct nvkm_memx *memx) +{ + nvkm_debug(&memx->pmu->subdev, " HOST UNBLOCKED\n"); + memx_cmd(memx, MEMX_LEAVE, 0, NULL); +} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h new file mode 100644 index 000000000..21abf31f4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PMU_PRIV_H__ +#define __NVKM_PMU_PRIV_H__ +#define nvkm_pmu(p) container_of((p), struct nvkm_pmu, subdev) +#include +#include +enum nvkm_acr_lsf_id; +struct nvkm_acr_lsfw; + +struct nvkm_pmu_func { + const struct nvkm_falcon_func *flcn; + + struct { + u32 *data; + u32 size; + } code; + + struct { + u32 *data; + u32 size; + } data; + + bool (*enabled)(struct nvkm_pmu *); + void (*reset)(struct nvkm_pmu *); + int (*init)(struct nvkm_pmu *); + void (*fini)(struct nvkm_pmu *); + void (*intr)(struct nvkm_pmu *); + int (*send)(struct nvkm_pmu *, u32 reply[2], u32 process, + u32 message, u32 data0, u32 data1); + void (*recv)(struct nvkm_pmu *); + int (*initmsg)(struct nvkm_pmu *); + void (*pgob)(struct nvkm_pmu *, bool); +}; + +extern const struct nvkm_falcon_func gt215_pmu_flcn; +int gt215_pmu_init(struct nvkm_pmu *); +void gt215_pmu_fini(struct nvkm_pmu *); +void gt215_pmu_intr(struct nvkm_pmu *); +void gt215_pmu_recv(struct nvkm_pmu *); +int gt215_pmu_send(struct nvkm_pmu *, u32[2], u32, u32, u32, u32); + +bool gf100_pmu_enabled(struct nvkm_pmu *); +void gf100_pmu_reset(struct nvkm_pmu *); +void gp102_pmu_reset(struct nvkm_pmu *pmu); + +void gk110_pmu_pgob(struct nvkm_pmu *, bool); + +extern const struct nvkm_falcon_func gm200_pmu_flcn; + +void gm20b_pmu_acr_bld_patch(struct nvkm_acr *, u32, s64); +void gm20b_pmu_acr_bld_write(struct nvkm_acr *, u32, struct nvkm_acr_lsfw *); +int gm20b_pmu_acr_boot(struct nvkm_falcon *); +int gm20b_pmu_acr_bootstrap_falcon(struct nvkm_falcon *, enum nvkm_acr_lsf_id); +void gm20b_pmu_recv(struct nvkm_pmu *); +int gm20b_pmu_initmsg(struct nvkm_pmu *); + +struct nvkm_pmu_fwif { + int version; + int (*load)(struct nvkm_pmu *, int ver, const struct nvkm_pmu_fwif *); + const struct nvkm_pmu_func *func; + const struct nvkm_acr_lsf_func *acr; +}; + +int gf100_pmu_nofw(struct nvkm_pmu *, int, const struct nvkm_pmu_fwif *); +int gm200_pmu_nofw(struct nvkm_pmu *, int, const struct nvkm_pmu_fwif *); +int gm20b_pmu_load(struct nvkm_pmu *, int, const struct nvkm_pmu_fwif *); + +int nvkm_pmu_ctor(const struct nvkm_pmu_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_pmu *); +int nvkm_pmu_new_(const struct nvkm_pmu_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_pmu **); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/Kbuild new file mode 100644 index 000000000..d47d1bdd0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/Kbuild @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/privring/gf100.o +nvkm-y += nvkm/subdev/privring/gf117.o +nvkm-y += nvkm/subdev/privring/gk104.o +nvkm-y += nvkm/subdev/privring/gk20a.o +nvkm-y += nvkm/subdev/privring/gm200.o +nvkm-y += nvkm/subdev/privring/gp10b.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf100.c new file mode 100644 index 000000000..ef7caca70 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf100.c @@ -0,0 +1,122 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include + +static void +gf100_privring_intr_hub(struct nvkm_subdev *privring, int i) +{ + struct nvkm_device *device = privring->device; + u32 addr = nvkm_rd32(device, 0x122120 + (i * 0x0400)); + u32 data = nvkm_rd32(device, 0x122124 + (i * 0x0400)); + u32 stat = nvkm_rd32(device, 0x122128 + (i * 0x0400)); + nvkm_debug(privring, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat); +} + +static void +gf100_privring_intr_rop(struct nvkm_subdev *privring, int i) +{ + struct nvkm_device *device = privring->device; + u32 addr = nvkm_rd32(device, 0x124120 + (i * 0x0400)); + u32 data = nvkm_rd32(device, 0x124124 + (i * 0x0400)); + u32 stat = nvkm_rd32(device, 0x124128 + (i * 0x0400)); + nvkm_debug(privring, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat); +} + +static void +gf100_privring_intr_gpc(struct nvkm_subdev *privring, int i) +{ + struct nvkm_device *device = privring->device; + u32 addr = nvkm_rd32(device, 0x128120 + (i * 0x0400)); + u32 data = nvkm_rd32(device, 0x128124 + (i * 0x0400)); + u32 stat = nvkm_rd32(device, 0x128128 + (i * 0x0400)); + nvkm_debug(privring, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat); +} + +void +gf100_privring_intr(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + u32 intr0 = nvkm_rd32(device, 0x121c58); + u32 intr1 = nvkm_rd32(device, 0x121c5c); + u32 hubnr = nvkm_rd32(device, 0x121c70); + u32 ropnr = nvkm_rd32(device, 0x121c74); + u32 gpcnr = nvkm_rd32(device, 0x121c78); + u32 i; + + for (i = 0; (intr0 & 0x0000ff00) && i < hubnr; i++) { + u32 stat = 0x00000100 << i; + if (intr0 & stat) { + gf100_privring_intr_hub(privring, i); + intr0 &= ~stat; + } + } + + for (i = 0; (intr0 & 0xffff0000) && i < ropnr; i++) { + u32 stat = 0x00010000 << i; + if (intr0 & stat) { + gf100_privring_intr_rop(privring, i); + intr0 &= ~stat; + } + } + + for (i = 0; intr1 && i < gpcnr; i++) { + u32 stat = 0x00000001 << i; + if (intr1 & stat) { + gf100_privring_intr_gpc(privring, i); + intr1 &= ~stat; + } + } + + nvkm_mask(device, 0x121c4c, 0x0000003f, 0x00000002); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x121c4c) & 0x0000003f)) + break; + ); +} + +static int +gf100_privring_init(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + nvkm_mask(device, 0x122310, 0x0003ffff, 0x00000800); + nvkm_wr32(device, 0x12232c, 0x00100064); + nvkm_wr32(device, 0x122330, 0x00100064); + nvkm_wr32(device, 0x122334, 0x00100064); + nvkm_mask(device, 0x122348, 0x0003ffff, 0x00000100); + return 0; +} + +static const struct nvkm_subdev_func +gf100_privring = { + .init = gf100_privring_init, + .intr = gf100_privring_intr, +}; + +int +gf100_privring_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_subdev **pprivring) +{ + return nvkm_subdev_new_(&gf100_privring, device, type, inst, pprivring); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf117.c b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf117.c new file mode 100644 index 000000000..c78721fcd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gf117.c @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Samuel Pitosiet + * + * 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: Samuel Pitoiset + */ +#include "priv.h" + +static int +gf117_privring_init(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + nvkm_mask(device, 0x122310, 0x0003ffff, 0x00000800); + nvkm_mask(device, 0x122348, 0x0003ffff, 0x00000100); + nvkm_mask(device, 0x1223b0, 0x0003ffff, 0x00000fff); + return 0; +} + +static const struct nvkm_subdev_func +gf117_privring = { + .init = gf117_privring_init, + .intr = gf100_privring_intr, +}; + +int +gf117_privring_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_subdev **pprivring) +{ + return nvkm_subdev_new_(&gf117_privring, device, type, inst, pprivring); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk104.c new file mode 100644 index 000000000..568a4c099 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk104.c @@ -0,0 +1,125 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include + +static void +gk104_privring_intr_hub(struct nvkm_subdev *privring, int i) +{ + struct nvkm_device *device = privring->device; + u32 addr = nvkm_rd32(device, 0x122120 + (i * 0x0800)); + u32 data = nvkm_rd32(device, 0x122124 + (i * 0x0800)); + u32 stat = nvkm_rd32(device, 0x122128 + (i * 0x0800)); + nvkm_debug(privring, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat); +} + +static void +gk104_privring_intr_rop(struct nvkm_subdev *privring, int i) +{ + struct nvkm_device *device = privring->device; + u32 addr = nvkm_rd32(device, 0x124120 + (i * 0x0800)); + u32 data = nvkm_rd32(device, 0x124124 + (i * 0x0800)); + u32 stat = nvkm_rd32(device, 0x124128 + (i * 0x0800)); + nvkm_debug(privring, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat); +} + +static void +gk104_privring_intr_gpc(struct nvkm_subdev *privring, int i) +{ + struct nvkm_device *device = privring->device; + u32 addr = nvkm_rd32(device, 0x128120 + (i * 0x0800)); + u32 data = nvkm_rd32(device, 0x128124 + (i * 0x0800)); + u32 stat = nvkm_rd32(device, 0x128128 + (i * 0x0800)); + nvkm_debug(privring, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat); +} + +void +gk104_privring_intr(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + u32 intr0 = nvkm_rd32(device, 0x120058); + u32 intr1 = nvkm_rd32(device, 0x12005c); + u32 hubnr = nvkm_rd32(device, 0x120070); + u32 ropnr = nvkm_rd32(device, 0x120074); + u32 gpcnr = nvkm_rd32(device, 0x120078); + u32 i; + + for (i = 0; (intr0 & 0x0000ff00) && i < hubnr; i++) { + u32 stat = 0x00000100 << i; + if (intr0 & stat) { + gk104_privring_intr_hub(privring, i); + intr0 &= ~stat; + } + } + + for (i = 0; (intr0 & 0xffff0000) && i < ropnr; i++) { + u32 stat = 0x00010000 << i; + if (intr0 & stat) { + gk104_privring_intr_rop(privring, i); + intr0 &= ~stat; + } + } + + for (i = 0; intr1 && i < gpcnr; i++) { + u32 stat = 0x00000001 << i; + if (intr1 & stat) { + gk104_privring_intr_gpc(privring, i); + intr1 &= ~stat; + } + } + + nvkm_mask(device, 0x12004c, 0x0000003f, 0x00000002); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x12004c) & 0x0000003f)) + break; + ); +} + +static int +gk104_privring_init(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + nvkm_mask(device, 0x122318, 0x0003ffff, 0x00001000); + nvkm_mask(device, 0x12231c, 0x0003ffff, 0x00000200); + nvkm_mask(device, 0x122310, 0x0003ffff, 0x00000800); + nvkm_mask(device, 0x122348, 0x0003ffff, 0x00000100); + nvkm_mask(device, 0x1223b0, 0x0003ffff, 0x00000fff); + nvkm_mask(device, 0x122348, 0x0003ffff, 0x00000200); + nvkm_mask(device, 0x122358, 0x0003ffff, 0x00002880); + return 0; +} + +static const struct nvkm_subdev_func +gk104_privring = { + .preinit = gk104_privring_init, + .init = gk104_privring_init, + .intr = gk104_privring_intr, +}; + +int +gk104_privring_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_subdev **pprivring) +{ + return nvkm_subdev_new_(&gk104_privring, device, type, inst, pprivring); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk20a.c new file mode 100644 index 000000000..55e4a60d8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gk20a.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include + +static void +gk20a_privring_init_privring_ring(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + nvkm_mask(device, 0x137250, 0x3f, 0); + + nvkm_mask(device, 0x000200, 0x20, 0); + udelay(20); + nvkm_mask(device, 0x000200, 0x20, 0x20); + + nvkm_wr32(device, 0x12004c, 0x4); + nvkm_wr32(device, 0x122204, 0x2); + nvkm_rd32(device, 0x122204); + + /* + * Bug: increase clock timeout to avoid operation failure at high + * gpcclk rate. + */ + nvkm_wr32(device, 0x122354, 0x800); + nvkm_wr32(device, 0x128328, 0x800); + nvkm_wr32(device, 0x124320, 0x800); +} + +static void +gk20a_privring_intr(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + u32 status0 = nvkm_rd32(device, 0x120058); + + if (status0 & 0x7) { + nvkm_debug(privring, "resetting privring ring\n"); + gk20a_privring_init_privring_ring(privring); + } + + /* Acknowledge interrupt */ + nvkm_mask(device, 0x12004c, 0x2, 0x2); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x12004c) & 0x0000003f)) + break; + ); +} + +static int +gk20a_privring_init(struct nvkm_subdev *privring) +{ + gk20a_privring_init_privring_ring(privring); + return 0; +} + +static const struct nvkm_subdev_func +gk20a_privring = { + .init = gk20a_privring_init, + .intr = gk20a_privring_intr, +}; + +int +gk20a_privring_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_subdev **pprivring) +{ + return nvkm_subdev_new_(&gk20a_privring, device, type, inst, pprivring); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gm200.c new file mode 100644 index 000000000..b4eaf6db3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gm200.c @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_subdev_func +gm200_privring = { + .intr = gk104_privring_intr, +}; + +int +gm200_privring_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_subdev **pprivring) +{ + return nvkm_subdev_new_(&gm200_privring, device, type, inst, pprivring); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gp10b.c new file mode 100644 index 000000000..4534111cf --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/gp10b.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include + +#include "priv.h" + +static int +gp10b_privring_init(struct nvkm_subdev *privring) +{ + struct nvkm_device *device = privring->device; + + nvkm_wr32(device, 0x1200a8, 0x0); + + /* init ring */ + nvkm_wr32(device, 0x12004c, 0x4); + nvkm_wr32(device, 0x122204, 0x2); + nvkm_rd32(device, 0x122204); + + /* timeout configuration */ + nvkm_wr32(device, 0x009080, 0x800186a0); + + return 0; +} + +static const struct nvkm_subdev_func +gp10b_privring = { + .init = gp10b_privring_init, + .intr = gk104_privring_intr, +}; + +int +gp10b_privring_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_subdev **pprivring) +{ + return nvkm_subdev_new_(&gp10b_privring, device, type, inst, pprivring); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/privring/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/priv.h new file mode 100644 index 000000000..b378c14bc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/privring/priv.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PRIVRING_PRIV_H__ +#define __NVKM_PRIVRING_PRIV_H__ +#include + +void gf100_privring_intr(struct nvkm_subdev *); +void gk104_privring_intr(struct nvkm_subdev *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild new file mode 100644 index 000000000..9aa76a2be --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/therm/base.o +nvkm-y += nvkm/subdev/therm/fan.o +nvkm-y += nvkm/subdev/therm/fannil.o +nvkm-y += nvkm/subdev/therm/fanpwm.o +nvkm-y += nvkm/subdev/therm/fantog.o +nvkm-y += nvkm/subdev/therm/ic.o +nvkm-y += nvkm/subdev/therm/temp.o +nvkm-y += nvkm/subdev/therm/nv40.o +nvkm-y += nvkm/subdev/therm/nv50.o +nvkm-y += nvkm/subdev/therm/g84.o +nvkm-y += nvkm/subdev/therm/gt215.o +nvkm-y += nvkm/subdev/therm/gf100.o +nvkm-y += nvkm/subdev/therm/gf119.o +nvkm-y += nvkm/subdev/therm/gk104.o +nvkm-y += nvkm/subdev/therm/gm107.o +nvkm-y += nvkm/subdev/therm/gm200.o +nvkm-y += nvkm/subdev/therm/gp100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c new file mode 100644 index 000000000..fc5ee118e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c @@ -0,0 +1,455 @@ +/* + * Copyright 2012 The Nouveau community + * + * 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: Martin Peres + */ +#include "priv.h" + +#include +#include + +int +nvkm_therm_temp_get(struct nvkm_therm *therm) +{ + if (therm->func->temp_get) + return therm->func->temp_get(therm); + return -ENODEV; +} + +static int +nvkm_therm_update_trip(struct nvkm_therm *therm) +{ + struct nvbios_therm_trip_point *trip = therm->fan->bios.trip, + *cur_trip = NULL, + *last_trip = therm->last_trip; + u8 temp = therm->func->temp_get(therm); + u16 duty, i; + + /* look for the trip point corresponding to the current temperature */ + cur_trip = NULL; + for (i = 0; i < therm->fan->bios.nr_fan_trip; i++) { + if (temp >= trip[i].temp) + cur_trip = &trip[i]; + } + + /* account for the hysteresis cycle */ + if (last_trip && temp <= (last_trip->temp) && + temp > (last_trip->temp - last_trip->hysteresis)) + cur_trip = last_trip; + + if (cur_trip) { + duty = cur_trip->fan_duty; + therm->last_trip = cur_trip; + } else { + duty = 0; + therm->last_trip = NULL; + } + + return duty; +} + +static int +nvkm_therm_compute_linear_duty(struct nvkm_therm *therm, u8 linear_min_temp, + u8 linear_max_temp) +{ + u8 temp = therm->func->temp_get(therm); + u16 duty; + + /* handle the non-linear part first */ + if (temp < linear_min_temp) + return therm->fan->bios.min_duty; + else if (temp > linear_max_temp) + return therm->fan->bios.max_duty; + + /* we are in the linear zone */ + duty = (temp - linear_min_temp); + duty *= (therm->fan->bios.max_duty - therm->fan->bios.min_duty); + duty /= (linear_max_temp - linear_min_temp); + duty += therm->fan->bios.min_duty; + return duty; +} + +static int +nvkm_therm_update_linear(struct nvkm_therm *therm) +{ + u8 min = therm->fan->bios.linear_min_temp; + u8 max = therm->fan->bios.linear_max_temp; + return nvkm_therm_compute_linear_duty(therm, min, max); +} + +static int +nvkm_therm_update_linear_fallback(struct nvkm_therm *therm) +{ + u8 max = therm->bios_sensor.thrs_fan_boost.temp; + return nvkm_therm_compute_linear_duty(therm, 30, max); +} + +static void +nvkm_therm_update(struct nvkm_therm *therm, int mode) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_timer *tmr = subdev->device->timer; + unsigned long flags; + bool immd = true; + bool poll = true; + int duty = -1; + + spin_lock_irqsave(&therm->lock, flags); + if (mode < 0) + mode = therm->mode; + therm->mode = mode; + + switch (mode) { + case NVKM_THERM_CTRL_MANUAL: + nvkm_timer_alarm(tmr, 0, &therm->alarm); + duty = nvkm_therm_fan_get(therm); + if (duty < 0) + duty = 100; + poll = false; + break; + case NVKM_THERM_CTRL_AUTO: + switch(therm->fan->bios.fan_mode) { + case NVBIOS_THERM_FAN_TRIP: + duty = nvkm_therm_update_trip(therm); + break; + case NVBIOS_THERM_FAN_LINEAR: + duty = nvkm_therm_update_linear(therm); + break; + case NVBIOS_THERM_FAN_OTHER: + if (therm->cstate) { + duty = therm->cstate; + poll = false; + } else { + duty = nvkm_therm_update_linear_fallback(therm); + } + break; + } + immd = false; + break; + case NVKM_THERM_CTRL_NONE: + default: + nvkm_timer_alarm(tmr, 0, &therm->alarm); + poll = false; + } + + if (poll) + nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm); + spin_unlock_irqrestore(&therm->lock, flags); + + if (duty >= 0) { + nvkm_debug(subdev, "FAN target request: %d%%\n", duty); + nvkm_therm_fan_set(therm, immd, duty); + } +} + +int +nvkm_therm_cstate(struct nvkm_therm *therm, int fan, int dir) +{ + struct nvkm_subdev *subdev = &therm->subdev; + if (!dir || (dir < 0 && fan < therm->cstate) || + (dir > 0 && fan > therm->cstate)) { + nvkm_debug(subdev, "default fan speed -> %d%%\n", fan); + therm->cstate = fan; + nvkm_therm_update(therm, -1); + } + return 0; +} + +static void +nvkm_therm_alarm(struct nvkm_alarm *alarm) +{ + struct nvkm_therm *therm = + container_of(alarm, struct nvkm_therm, alarm); + nvkm_therm_update(therm, -1); +} + +int +nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + static const char *name[] = { + "disabled", + "manual", + "automatic" + }; + + /* The default PPWR ucode on fermi interferes with fan management */ + if ((mode >= ARRAY_SIZE(name)) || + (mode != NVKM_THERM_CTRL_NONE && nvkm_pmu_fan_controlled(device))) + return -EINVAL; + + /* do not allow automatic fan management if the thermal sensor is + * not available */ + if (mode == NVKM_THERM_CTRL_AUTO && + therm->func->temp_get(therm) < 0) + return -EINVAL; + + if (therm->mode == mode) + return 0; + + nvkm_debug(subdev, "fan management: %s\n", name[mode]); + nvkm_therm_update(therm, mode); + return 0; +} + +int +nvkm_therm_attr_get(struct nvkm_therm *therm, enum nvkm_therm_attr_type type) +{ + switch (type) { + case NVKM_THERM_ATTR_FAN_MIN_DUTY: + return therm->fan->bios.min_duty; + case NVKM_THERM_ATTR_FAN_MAX_DUTY: + return therm->fan->bios.max_duty; + case NVKM_THERM_ATTR_FAN_MODE: + return therm->mode; + case NVKM_THERM_ATTR_THRS_FAN_BOOST: + return therm->bios_sensor.thrs_fan_boost.temp; + case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST: + return therm->bios_sensor.thrs_fan_boost.hysteresis; + case NVKM_THERM_ATTR_THRS_DOWN_CLK: + return therm->bios_sensor.thrs_down_clock.temp; + case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST: + return therm->bios_sensor.thrs_down_clock.hysteresis; + case NVKM_THERM_ATTR_THRS_CRITICAL: + return therm->bios_sensor.thrs_critical.temp; + case NVKM_THERM_ATTR_THRS_CRITICAL_HYST: + return therm->bios_sensor.thrs_critical.hysteresis; + case NVKM_THERM_ATTR_THRS_SHUTDOWN: + return therm->bios_sensor.thrs_shutdown.temp; + case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST: + return therm->bios_sensor.thrs_shutdown.hysteresis; + } + + return -EINVAL; +} + +int +nvkm_therm_attr_set(struct nvkm_therm *therm, + enum nvkm_therm_attr_type type, int value) +{ + switch (type) { + case NVKM_THERM_ATTR_FAN_MIN_DUTY: + if (value < 0) + value = 0; + if (value > therm->fan->bios.max_duty) + value = therm->fan->bios.max_duty; + therm->fan->bios.min_duty = value; + return 0; + case NVKM_THERM_ATTR_FAN_MAX_DUTY: + if (value < 0) + value = 0; + if (value < therm->fan->bios.min_duty) + value = therm->fan->bios.min_duty; + therm->fan->bios.max_duty = value; + return 0; + case NVKM_THERM_ATTR_FAN_MODE: + return nvkm_therm_fan_mode(therm, value); + case NVKM_THERM_ATTR_THRS_FAN_BOOST: + therm->bios_sensor.thrs_fan_boost.temp = value; + therm->func->program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST: + therm->bios_sensor.thrs_fan_boost.hysteresis = value; + therm->func->program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_DOWN_CLK: + therm->bios_sensor.thrs_down_clock.temp = value; + therm->func->program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST: + therm->bios_sensor.thrs_down_clock.hysteresis = value; + therm->func->program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_CRITICAL: + therm->bios_sensor.thrs_critical.temp = value; + therm->func->program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_CRITICAL_HYST: + therm->bios_sensor.thrs_critical.hysteresis = value; + therm->func->program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_SHUTDOWN: + therm->bios_sensor.thrs_shutdown.temp = value; + therm->func->program_alarms(therm); + return 0; + case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST: + therm->bios_sensor.thrs_shutdown.hysteresis = value; + therm->func->program_alarms(therm); + return 0; + } + + return -EINVAL; +} + +void +nvkm_therm_clkgate_enable(struct nvkm_therm *therm) +{ + if (!therm || !therm->func->clkgate_enable || !therm->clkgating_enabled) + return; + + nvkm_debug(&therm->subdev, + "Enabling clockgating\n"); + therm->func->clkgate_enable(therm); +} + +void +nvkm_therm_clkgate_fini(struct nvkm_therm *therm, bool suspend) +{ + if (!therm || !therm->func->clkgate_fini || !therm->clkgating_enabled) + return; + + nvkm_debug(&therm->subdev, + "Preparing clockgating for %s\n", + suspend ? "suspend" : "fini"); + therm->func->clkgate_fini(therm, suspend); +} + +static void +nvkm_therm_clkgate_oneinit(struct nvkm_therm *therm) +{ + if (!therm->func->clkgate_enable || !therm->clkgating_enabled) + return; + + nvkm_info(&therm->subdev, "Clockgating enabled\n"); +} + +static void +nvkm_therm_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_therm *therm = nvkm_therm(subdev); + if (therm->func->intr) + therm->func->intr(therm); +} + +static int +nvkm_therm_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_therm *therm = nvkm_therm(subdev); + + if (therm->func->fini) + therm->func->fini(therm); + + nvkm_therm_fan_fini(therm, suspend); + nvkm_therm_sensor_fini(therm, suspend); + + if (suspend) { + therm->suspend = therm->mode; + therm->mode = NVKM_THERM_CTRL_NONE; + } + + return 0; +} + +static int +nvkm_therm_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_therm *therm = nvkm_therm(subdev); + nvkm_therm_sensor_ctor(therm); + nvkm_therm_ic_ctor(therm); + nvkm_therm_fan_ctor(therm); + nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO); + nvkm_therm_sensor_preinit(therm); + nvkm_therm_clkgate_oneinit(therm); + return 0; +} + +static int +nvkm_therm_init(struct nvkm_subdev *subdev) +{ + struct nvkm_therm *therm = nvkm_therm(subdev); + + if (therm->func->init) + therm->func->init(therm); + + if (therm->suspend >= 0) { + /* restore the pwm value only when on manual or auto mode */ + if (therm->suspend > 0) + nvkm_therm_fan_set(therm, true, therm->fan->percent); + + nvkm_therm_fan_mode(therm, therm->suspend); + } + + nvkm_therm_sensor_init(therm); + nvkm_therm_fan_init(therm); + return 0; +} + +void +nvkm_therm_clkgate_init(struct nvkm_therm *therm, + const struct nvkm_therm_clkgate_pack *p) +{ + if (!therm || !therm->func->clkgate_init || !therm->clkgating_enabled) + return; + + therm->func->clkgate_init(therm, p); +} + +static void * +nvkm_therm_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_therm *therm = nvkm_therm(subdev); + kfree(therm->fan); + return therm; +} + +static const struct nvkm_subdev_func +nvkm_therm = { + .dtor = nvkm_therm_dtor, + .oneinit = nvkm_therm_oneinit, + .init = nvkm_therm_init, + .fini = nvkm_therm_fini, + .intr = nvkm_therm_intr, +}; + +void +nvkm_therm_ctor(struct nvkm_therm *therm, struct nvkm_device *device, enum nvkm_subdev_type type, + int inst, const struct nvkm_therm_func *func) +{ + nvkm_subdev_ctor(&nvkm_therm, device, type, inst, &therm->subdev); + therm->func = func; + + nvkm_alarm_init(&therm->alarm, nvkm_therm_alarm); + spin_lock_init(&therm->lock); + spin_lock_init(&therm->sensor.alarm_program_lock); + + therm->fan_get = nvkm_therm_fan_user_get; + therm->fan_set = nvkm_therm_fan_user_set; + therm->attr_get = nvkm_therm_attr_get; + therm->attr_set = nvkm_therm_attr_set; + therm->mode = therm->suspend = -1; /* undefined */ + + therm->clkgating_enabled = nvkm_boolopt(device->cfgopt, + "NvPmEnableGating", false); +} + +int +nvkm_therm_new_(const struct nvkm_therm_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_therm **ptherm) +{ + struct nvkm_therm *therm; + + if (!(therm = *ptherm = kzalloc(sizeof(*therm), GFP_KERNEL))) + return -ENOMEM; + + nvkm_therm_ctor(therm, device, type, inst, func); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c new file mode 100644 index 000000000..f8fa43c8a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c @@ -0,0 +1,279 @@ +/* + * 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 + * Martin Peres + */ +#include "priv.h" + +#include +#include +#include + +static int +nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target) +{ + struct nvkm_therm *therm = fan->parent; + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_timer *tmr = subdev->device->timer; + unsigned long flags; + int ret = 0; + int duty; + + /* update target fan speed, restricting to allowed range */ + spin_lock_irqsave(&fan->lock, flags); + if (target < 0) + target = fan->percent; + target = max_t(u8, target, fan->bios.min_duty); + target = min_t(u8, target, fan->bios.max_duty); + if (fan->percent != target) { + nvkm_debug(subdev, "FAN target: %d\n", target); + fan->percent = target; + } + + /* check that we're not already at the target duty cycle */ + duty = fan->get(therm); + if (duty == target) { + spin_unlock_irqrestore(&fan->lock, flags); + return 0; + } + + /* smooth out the fanspeed increase/decrease */ + if (!immediate && duty >= 0) { + /* the constant "3" is a rough approximation taken from + * nvidia's behaviour. + * it is meant to bump the fan speed more incrementally + */ + if (duty < target) + duty = min(duty + 3, target); + else if (duty > target) + duty = max(duty - 3, target); + } else { + duty = target; + } + + nvkm_debug(subdev, "FAN update: %d\n", duty); + ret = fan->set(therm, duty); + if (ret) { + spin_unlock_irqrestore(&fan->lock, flags); + return ret; + } + + /* fan speed updated, drop the fan lock before grabbing the + * alarm-scheduling lock and risking a deadlock + */ + spin_unlock_irqrestore(&fan->lock, flags); + + /* schedule next fan update, if not at target speed already */ + if (target != duty) { + u16 bump_period = fan->bios.bump_period; + u16 slow_down_period = fan->bios.slow_down_period; + u64 delay; + + if (duty > target) + delay = slow_down_period; + else if (duty == target) + delay = min(bump_period, slow_down_period) ; + else + delay = bump_period; + + nvkm_timer_alarm(tmr, delay * 1000 * 1000, &fan->alarm); + } + + return ret; +} + +static void +nvkm_fan_alarm(struct nvkm_alarm *alarm) +{ + struct nvkm_fan *fan = container_of(alarm, struct nvkm_fan, alarm); + nvkm_fan_update(fan, false, -1); +} + +int +nvkm_therm_fan_get(struct nvkm_therm *therm) +{ + return therm->fan->get(therm); +} + +int +nvkm_therm_fan_set(struct nvkm_therm *therm, bool immediate, int percent) +{ + return nvkm_fan_update(therm->fan, immediate, percent); +} + +int +nvkm_therm_fan_sense(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + struct nvkm_timer *tmr = device->timer; + struct nvkm_gpio *gpio = device->gpio; + u32 cycles, cur, prev; + u64 start, end, tach; + + if (therm->func->fan_sense) + return therm->func->fan_sense(therm); + + if (therm->fan->tach.func == DCB_GPIO_UNUSED) + return -ENODEV; + + /* Time a complete rotation and extrapolate to RPM: + * When the fan spins, it changes the value of GPIO FAN_SENSE. + * We get 4 changes (0 -> 1 -> 0 -> 1) per complete rotation. + */ + start = nvkm_timer_read(tmr); + prev = nvkm_gpio_get(gpio, 0, therm->fan->tach.func, + therm->fan->tach.line); + cycles = 0; + do { + usleep_range(500, 1000); /* supports 0 < rpm < 7500 */ + + cur = nvkm_gpio_get(gpio, 0, therm->fan->tach.func, + therm->fan->tach.line); + if (prev != cur) { + if (!start) + start = nvkm_timer_read(tmr); + cycles++; + prev = cur; + } + } while (cycles < 5 && nvkm_timer_read(tmr) - start < 250000000); + end = nvkm_timer_read(tmr); + + if (cycles == 5) { + tach = (u64)60000000000ULL; + do_div(tach, (end - start)); + return tach; + } else + return 0; +} + +int +nvkm_therm_fan_user_get(struct nvkm_therm *therm) +{ + return nvkm_therm_fan_get(therm); +} + +int +nvkm_therm_fan_user_set(struct nvkm_therm *therm, int percent) +{ + if (therm->mode != NVKM_THERM_CTRL_MANUAL) + return -EINVAL; + + return nvkm_therm_fan_set(therm, true, percent); +} + +static void +nvkm_therm_fan_set_defaults(struct nvkm_therm *therm) +{ + therm->fan->bios.pwm_freq = 0; + therm->fan->bios.min_duty = 0; + therm->fan->bios.max_duty = 100; + therm->fan->bios.bump_period = 500; + therm->fan->bios.slow_down_period = 2000; + therm->fan->bios.linear_min_temp = 40; + therm->fan->bios.linear_max_temp = 85; +} + +static void +nvkm_therm_fan_safety_checks(struct nvkm_therm *therm) +{ + if (therm->fan->bios.min_duty > 100) + therm->fan->bios.min_duty = 100; + if (therm->fan->bios.max_duty > 100) + therm->fan->bios.max_duty = 100; + + if (therm->fan->bios.min_duty > therm->fan->bios.max_duty) + therm->fan->bios.min_duty = therm->fan->bios.max_duty; +} + +int +nvkm_therm_fan_init(struct nvkm_therm *therm) +{ + return 0; +} + +int +nvkm_therm_fan_fini(struct nvkm_therm *therm, bool suspend) +{ + struct nvkm_timer *tmr = therm->subdev.device->timer; + if (suspend) + nvkm_timer_alarm(tmr, 0, &therm->fan->alarm); + return 0; +} + +int +nvkm_therm_fan_ctor(struct nvkm_therm *therm) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_gpio *gpio = device->gpio; + struct nvkm_bios *bios = device->bios; + struct dcb_gpio_func func; + int ret; + + /* attempt to locate a drivable fan, and determine control method */ + ret = nvkm_gpio_find(gpio, 0, DCB_GPIO_FAN, 0xff, &func); + if (ret == 0) { + /* FIXME: is this really the place to perform such checks ? */ + if (func.line != 16 && func.log[0] & DCB_GPIO_LOG_DIR_IN) { + nvkm_debug(subdev, "GPIO_FAN is in input mode\n"); + ret = -EINVAL; + } else { + ret = nvkm_fanpwm_create(therm, &func); + if (ret != 0) + ret = nvkm_fantog_create(therm, &func); + } + } + + /* no controllable fan found, create a dummy fan module */ + if (ret != 0) { + ret = nvkm_fannil_create(therm); + if (ret) + return ret; + } + + nvkm_debug(subdev, "FAN control: %s\n", therm->fan->type); + + /* read the current speed, it is useful when resuming */ + therm->fan->percent = nvkm_therm_fan_get(therm); + + /* attempt to detect a tachometer connection */ + ret = nvkm_gpio_find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, + &therm->fan->tach); + if (ret) + therm->fan->tach.func = DCB_GPIO_UNUSED; + + /* initialise fan bump/slow update handling */ + therm->fan->parent = therm; + nvkm_alarm_init(&therm->fan->alarm, nvkm_fan_alarm); + spin_lock_init(&therm->fan->lock); + + /* other random init... */ + nvkm_therm_fan_set_defaults(therm); + nvbios_perf_fan_parse(bios, &therm->fan->perf); + if (!nvbios_fan_parse(bios, &therm->fan->bios)) { + nvkm_debug(subdev, "parsing the fan table failed\n"); + if (nvbios_therm_fan_parse(bios, &therm->fan->bios)) + nvkm_error(subdev, "parsing both fan tables failed\n"); + } + nvkm_therm_fan_safety_checks(therm); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c new file mode 100644 index 000000000..8ae300f91 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c @@ -0,0 +1,52 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static int +nvkm_fannil_get(struct nvkm_therm *therm) +{ + return -ENODEV; +} + +static int +nvkm_fannil_set(struct nvkm_therm *therm, int percent) +{ + return -ENODEV; +} + +int +nvkm_fannil_create(struct nvkm_therm *therm) +{ + struct nvkm_fan *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + therm->fan = priv; + if (!priv) + return -ENOMEM; + + priv->type = "none / external"; + priv->get = nvkm_fannil_get; + priv->set = nvkm_fannil_set; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c new file mode 100644 index 000000000..340f37a29 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c @@ -0,0 +1,110 @@ +/* + * 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 + * Martin Peres + */ +#include "priv.h" + +#include +#include +#include +#include + +struct nvkm_fanpwm { + struct nvkm_fan base; + struct dcb_gpio_func func; +}; + +static int +nvkm_fanpwm_get(struct nvkm_therm *therm) +{ + struct nvkm_fanpwm *fan = (void *)therm->fan; + struct nvkm_device *device = therm->subdev.device; + struct nvkm_gpio *gpio = device->gpio; + int card_type = device->card_type; + u32 divs, duty; + int ret; + + ret = therm->func->pwm_get(therm, fan->func.line, &divs, &duty); + if (ret == 0 && divs) { + divs = max(divs, duty); + if (card_type <= NV_40 || (fan->func.log[0] & 1)) + duty = divs - duty; + return (duty * 100) / divs; + } + + return nvkm_gpio_get(gpio, 0, fan->func.func, fan->func.line) * 100; +} + +static int +nvkm_fanpwm_set(struct nvkm_therm *therm, int percent) +{ + struct nvkm_fanpwm *fan = (void *)therm->fan; + int card_type = therm->subdev.device->card_type; + u32 divs, duty; + int ret; + + divs = fan->base.perf.pwm_divisor; + if (fan->base.bios.pwm_freq) { + divs = 1; + if (therm->func->pwm_clock) + divs = therm->func->pwm_clock(therm, fan->func.line); + divs /= fan->base.bios.pwm_freq; + } + + duty = ((divs * percent) + 99) / 100; + if (card_type <= NV_40 || (fan->func.log[0] & 1)) + duty = divs - duty; + + ret = therm->func->pwm_set(therm, fan->func.line, divs, duty); + if (ret == 0) + ret = therm->func->pwm_ctrl(therm, fan->func.line, true); + return ret; +} + +int +nvkm_fanpwm_create(struct nvkm_therm *therm, struct dcb_gpio_func *func) +{ + struct nvkm_device *device = therm->subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvkm_fanpwm *fan; + struct nvbios_therm_fan info = {}; + u32 divs, duty; + + nvbios_fan_parse(bios, &info); + + if (!nvkm_boolopt(device->cfgopt, "NvFanPWM", func->param) || + !therm->func->pwm_ctrl || info.type == NVBIOS_THERM_FAN_TOGGLE || + therm->func->pwm_get(therm, func->line, &divs, &duty) == -ENODEV) + return -ENODEV; + + fan = kzalloc(sizeof(*fan), GFP_KERNEL); + therm->fan = &fan->base; + if (!fan) + return -ENOMEM; + + fan->base.type = "PWM"; + fan->base.get = nvkm_fanpwm_get; + fan->base.set = nvkm_fanpwm_set; + fan->func = *func; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c new file mode 100644 index 000000000..ff9fbe795 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c @@ -0,0 +1,116 @@ +/* + * Copyright 2012 The Nouveau community + * + * 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: Martin Peres + */ +#include "priv.h" + +#include +#include + +struct nvkm_fantog { + struct nvkm_fan base; + struct nvkm_alarm alarm; + spinlock_t lock; + u32 period_us; + u32 percent; + struct dcb_gpio_func func; +}; + +static void +nvkm_fantog_update(struct nvkm_fantog *fan, int percent) +{ + struct nvkm_therm *therm = fan->base.parent; + struct nvkm_device *device = therm->subdev.device; + struct nvkm_timer *tmr = device->timer; + struct nvkm_gpio *gpio = device->gpio; + unsigned long flags; + int duty; + + spin_lock_irqsave(&fan->lock, flags); + if (percent < 0) + percent = fan->percent; + fan->percent = percent; + + duty = !nvkm_gpio_get(gpio, 0, DCB_GPIO_FAN, 0xff); + nvkm_gpio_set(gpio, 0, DCB_GPIO_FAN, 0xff, duty); + + if (percent != (duty * 100)) { + u64 next_change = (percent * fan->period_us) / 100; + if (!duty) + next_change = fan->period_us - next_change; + nvkm_timer_alarm(tmr, next_change * 1000, &fan->alarm); + } + spin_unlock_irqrestore(&fan->lock, flags); +} + +static void +nvkm_fantog_alarm(struct nvkm_alarm *alarm) +{ + struct nvkm_fantog *fan = + container_of(alarm, struct nvkm_fantog, alarm); + nvkm_fantog_update(fan, -1); +} + +static int +nvkm_fantog_get(struct nvkm_therm *therm) +{ + struct nvkm_fantog *fan = (void *)therm->fan; + return fan->percent; +} + +static int +nvkm_fantog_set(struct nvkm_therm *therm, int percent) +{ + struct nvkm_fantog *fan = (void *)therm->fan; + if (therm->func->pwm_ctrl) + therm->func->pwm_ctrl(therm, fan->func.line, false); + nvkm_fantog_update(fan, percent); + return 0; +} + +int +nvkm_fantog_create(struct nvkm_therm *therm, struct dcb_gpio_func *func) +{ + struct nvkm_fantog *fan; + int ret; + + if (therm->func->pwm_ctrl) { + ret = therm->func->pwm_ctrl(therm, func->line, false); + if (ret) + return ret; + } + + fan = kzalloc(sizeof(*fan), GFP_KERNEL); + therm->fan = &fan->base; + if (!fan) + return -ENOMEM; + + fan->base.type = "toggle"; + fan->base.get = nvkm_fantog_get; + fan->base.set = nvkm_fantog_set; + nvkm_alarm_init(&fan->alarm, nvkm_fantog_alarm); + fan->period_us = 100000; /* 10Hz */ + fan->percent = 100; + fan->func = *func; + spin_lock_init(&fan->lock); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c new file mode 100644 index 000000000..4af86f2d3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c @@ -0,0 +1,247 @@ +/* + * 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 + * Martin Peres + */ +#include "priv.h" + +#include + +int +g84_temp_get(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + + if (nvkm_fuse_read(device->fuse, 0x1a8) == 1) + return nvkm_rd32(device, 0x20400); + else + return -ENODEV; +} + +void +g84_sensor_setup(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + + /* enable temperature reading for cards with insane defaults */ + if (nvkm_fuse_read(device->fuse, 0x1a8) == 1) { + nvkm_mask(device, 0x20008, 0x80008000, 0x80000000); + nvkm_mask(device, 0x2000c, 0x80000003, 0x00000000); + mdelay(20); /* wait for the temperature to stabilize */ + } +} + +static void +g84_therm_program_alarms(struct nvkm_therm *therm) +{ + struct nvbios_therm_sensor *sensor = &therm->bios_sensor; + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + unsigned long flags; + + spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags); + + /* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */ + nvkm_wr32(device, 0x20000, 0x000003ff); + + /* shutdown: The computer should be shutdown when reached */ + nvkm_wr32(device, 0x20484, sensor->thrs_shutdown.hysteresis); + nvkm_wr32(device, 0x20480, sensor->thrs_shutdown.temp); + + /* THRS_1 : fan boost*/ + nvkm_wr32(device, 0x204c4, sensor->thrs_fan_boost.temp); + + /* THRS_2 : critical */ + nvkm_wr32(device, 0x204c0, sensor->thrs_critical.temp); + + /* THRS_4 : down clock */ + nvkm_wr32(device, 0x20414, sensor->thrs_down_clock.temp); + spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags); + + nvkm_debug(subdev, + "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n", + sensor->thrs_fan_boost.temp, + sensor->thrs_fan_boost.hysteresis, + sensor->thrs_down_clock.temp, + sensor->thrs_down_clock.hysteresis, + sensor->thrs_critical.temp, + sensor->thrs_critical.hysteresis, + sensor->thrs_shutdown.temp, + sensor->thrs_shutdown.hysteresis); + +} + +/* must be called with alarm_program_lock taken ! */ +static void +g84_therm_threshold_hyst_emulation(struct nvkm_therm *therm, + uint32_t thrs_reg, u8 status_bit, + const struct nvbios_therm_threshold *thrs, + enum nvkm_therm_thrs thrs_name) +{ + struct nvkm_device *device = therm->subdev.device; + enum nvkm_therm_thrs_direction direction; + enum nvkm_therm_thrs_state prev_state, new_state; + int temp, cur; + + prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name); + temp = nvkm_rd32(device, thrs_reg); + + /* program the next threshold */ + if (temp == thrs->temp) { + nvkm_wr32(device, thrs_reg, thrs->temp - thrs->hysteresis); + new_state = NVKM_THERM_THRS_HIGHER; + } else { + nvkm_wr32(device, thrs_reg, thrs->temp); + new_state = NVKM_THERM_THRS_LOWER; + } + + /* fix the state (in case someone reprogrammed the alarms) */ + cur = therm->func->temp_get(therm); + if (new_state == NVKM_THERM_THRS_LOWER && cur > thrs->temp) + new_state = NVKM_THERM_THRS_HIGHER; + else if (new_state == NVKM_THERM_THRS_HIGHER && + cur < thrs->temp - thrs->hysteresis) + new_state = NVKM_THERM_THRS_LOWER; + nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state); + + /* find the direction */ + if (prev_state < new_state) + direction = NVKM_THERM_THRS_RISING; + else if (prev_state > new_state) + direction = NVKM_THERM_THRS_FALLING; + else + return; + + /* advertise a change in direction */ + nvkm_therm_sensor_event(therm, thrs_name, direction); +} + +static void +g84_therm_intr(struct nvkm_therm *therm) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + struct nvbios_therm_sensor *sensor = &therm->bios_sensor; + unsigned long flags; + uint32_t intr; + + spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags); + + intr = nvkm_rd32(device, 0x20100) & 0x3ff; + + /* THRS_4: downclock */ + if (intr & 0x002) { + g84_therm_threshold_hyst_emulation(therm, 0x20414, 24, + &sensor->thrs_down_clock, + NVKM_THERM_THRS_DOWNCLOCK); + intr &= ~0x002; + } + + /* shutdown */ + if (intr & 0x004) { + g84_therm_threshold_hyst_emulation(therm, 0x20480, 20, + &sensor->thrs_shutdown, + NVKM_THERM_THRS_SHUTDOWN); + intr &= ~0x004; + } + + /* THRS_1 : fan boost */ + if (intr & 0x008) { + g84_therm_threshold_hyst_emulation(therm, 0x204c4, 21, + &sensor->thrs_fan_boost, + NVKM_THERM_THRS_FANBOOST); + intr &= ~0x008; + } + + /* THRS_2 : critical */ + if (intr & 0x010) { + g84_therm_threshold_hyst_emulation(therm, 0x204c0, 22, + &sensor->thrs_critical, + NVKM_THERM_THRS_CRITICAL); + intr &= ~0x010; + } + + if (intr) + nvkm_error(subdev, "intr %08x\n", intr); + + /* ACK everything */ + nvkm_wr32(device, 0x20100, 0xffffffff); + nvkm_wr32(device, 0x1100, 0x10000); /* PBUS */ + + spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags); +} + +void +g84_therm_fini(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + + /* Disable PTherm IRQs */ + nvkm_wr32(device, 0x20000, 0x00000000); + + /* ACK all PTherm IRQs */ + nvkm_wr32(device, 0x20100, 0xffffffff); + nvkm_wr32(device, 0x1100, 0x10000); /* PBUS */ +} + +void +g84_therm_init(struct nvkm_therm *therm) +{ + g84_sensor_setup(therm); +} + +static const struct nvkm_therm_func +g84_therm = { + .init = g84_therm_init, + .fini = g84_therm_fini, + .intr = g84_therm_intr, + .pwm_ctrl = nv50_fan_pwm_ctrl, + .pwm_get = nv50_fan_pwm_get, + .pwm_set = nv50_fan_pwm_set, + .pwm_clock = nv50_fan_pwm_clock, + .temp_get = g84_temp_get, + .program_alarms = g84_therm_program_alarms, +}; + +int +g84_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + struct nvkm_therm *therm; + int ret; + + ret = nvkm_therm_new_(&g84_therm, device, type, inst, &therm); + *ptherm = therm; + if (ret) + return ret; + + /* init the thresholds */ + nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_SHUTDOWN, + NVKM_THERM_THRS_LOWER); + nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_FANBOOST, + NVKM_THERM_THRS_LOWER); + nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_CRITICAL, + NVKM_THERM_THRS_LOWER); + nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_DOWNCLOCK, + NVKM_THERM_THRS_LOWER); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.c new file mode 100644 index 000000000..5ae691332 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.c @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Lyude Paul + */ +#include + +#include "priv.h" + +#define pack_for_each_init(init, pack, head) \ + for (pack = head; pack && pack->init; pack++) \ + for (init = pack->init; init && init->count; init++) +void +gf100_clkgate_init(struct nvkm_therm *therm, + const struct nvkm_therm_clkgate_pack *p) +{ + struct nvkm_device *device = therm->subdev.device; + const struct nvkm_therm_clkgate_pack *pack; + const struct nvkm_therm_clkgate_init *init; + u32 next, addr; + + pack_for_each_init(init, pack, p) { + next = init->addr + init->count * 8; + addr = init->addr; + + nvkm_trace(&therm->subdev, "{ 0x%06x, %d, 0x%08x }\n", + init->addr, init->count, init->data); + while (addr < next) { + nvkm_trace(&therm->subdev, "\t0x%06x = 0x%08x\n", + addr, init->data); + nvkm_wr32(device, addr, init->data); + addr += 8; + } + } +} + +/* + * TODO: Fermi clockgating isn't understood fully yet, so we don't specify any + * clockgate functions to use + */ diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.h b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.h new file mode 100644 index 000000000..cfb25af77 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf100.h @@ -0,0 +1,35 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Lyude Paul + */ + +#ifndef __GF100_THERM_H__ +#define __GF100_THERM_H__ + +#include + +struct gf100_idle_filter { + u32 fecs; + u32 hubmmu; +}; + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c new file mode 100644 index 000000000..684aff743 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c @@ -0,0 +1,154 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static int +pwm_info(struct nvkm_therm *therm, int line) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + u32 gpio = nvkm_rd32(device, 0x00d610 + (line * 0x04)); + + switch (gpio & 0x000000c0) { + case 0x00000000: /* normal mode, possibly pwm forced off by us */ + case 0x00000040: /* nvio special */ + switch (gpio & 0x0000001f) { + case 0x00: return 2; + case 0x19: return 1; + case 0x1c: return 0; + case 0x1e: return 2; + default: + break; + } + break; + default: + break; + } + + nvkm_error(subdev, "GPIO %d unknown PWM: %08x\n", line, gpio); + return -ENODEV; +} + +int +gf119_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable) +{ + struct nvkm_device *device = therm->subdev.device; + u32 data = enable ? 0x00000040 : 0x00000000; + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + else if (indx < 2) + nvkm_mask(device, 0x00d610 + (line * 0x04), 0x000000c0, data); + /* nothing to do for indx == 2, it seems hardwired to PTHERM */ + return 0; +} + +int +gf119_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty) +{ + struct nvkm_device *device = therm->subdev.device; + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + else if (indx < 2) { + if (nvkm_rd32(device, 0x00d610 + (line * 0x04)) & 0x00000040) { + *divs = nvkm_rd32(device, 0x00e114 + (indx * 8)); + *duty = nvkm_rd32(device, 0x00e118 + (indx * 8)); + return 0; + } + } else if (indx == 2) { + *divs = nvkm_rd32(device, 0x0200d8) & 0x1fff; + *duty = nvkm_rd32(device, 0x0200dc) & 0x1fff; + return 0; + } + + return -EINVAL; +} + +int +gf119_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty) +{ + struct nvkm_device *device = therm->subdev.device; + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + else if (indx < 2) { + nvkm_wr32(device, 0x00e114 + (indx * 8), divs); + nvkm_wr32(device, 0x00e118 + (indx * 8), duty | 0x80000000); + } else if (indx == 2) { + nvkm_mask(device, 0x0200d8, 0x1fff, divs); /* keep the high bits */ + nvkm_wr32(device, 0x0200dc, duty | 0x40000000); + } + return 0; +} + +int +gf119_fan_pwm_clock(struct nvkm_therm *therm, int line) +{ + struct nvkm_device *device = therm->subdev.device; + int indx = pwm_info(therm, line); + if (indx < 0) + return 0; + else if (indx < 2) + return (device->crystal * 1000) / 20; + else + return device->crystal * 1000 / 10; +} + +void +gf119_therm_init(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + + g84_sensor_setup(therm); + + /* enable fan tach, count revolutions per-second */ + nvkm_mask(device, 0x00e720, 0x00000003, 0x00000002); + if (therm->fan->tach.func != DCB_GPIO_UNUSED) { + nvkm_mask(device, 0x00d79c, 0x000000ff, therm->fan->tach.line); + nvkm_wr32(device, 0x00e724, device->crystal * 1000); + nvkm_mask(device, 0x00e720, 0x00000001, 0x00000001); + } + nvkm_mask(device, 0x00e720, 0x00000002, 0x00000000); +} + +static const struct nvkm_therm_func +gf119_therm = { + .init = gf119_therm_init, + .fini = g84_therm_fini, + .pwm_ctrl = gf119_fan_pwm_ctrl, + .pwm_get = gf119_fan_pwm_get, + .pwm_set = gf119_fan_pwm_set, + .pwm_clock = gf119_fan_pwm_clock, + .temp_get = g84_temp_get, + .fan_sense = gt215_therm_fan_sense, + .program_alarms = nvkm_therm_program_alarms_polling, +}; + +int +gf119_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + return nvkm_therm_new_(&gf119_therm, device, type, inst, ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.c new file mode 100644 index 000000000..45e295c27 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.c @@ -0,0 +1,133 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Lyude Paul + */ +#include + +#include "priv.h" +#include "gk104.h" + +void +gk104_clkgate_enable(struct nvkm_therm *base) +{ + struct gk104_therm *therm = gk104_therm(base); + struct nvkm_device *dev = therm->base.subdev.device; + const struct gk104_clkgate_engine_info *order = therm->clkgate_order; + int i; + + /* Program ENG_MANT, ENG_FILTER */ + for (i = 0; order[i].type != NVKM_SUBDEV_NR; i++) { + if (!nvkm_device_subdev(dev, order[i].type, order[i].inst)) + continue; + + nvkm_mask(dev, 0x20200 + order[i].offset, 0xff00, 0x4500); + } + + /* magic */ + nvkm_wr32(dev, 0x020288, therm->idle_filter->fecs); + nvkm_wr32(dev, 0x02028c, therm->idle_filter->hubmmu); + + /* Enable clockgating (ENG_CLK = RUN->AUTO) */ + for (i = 0; order[i].type != NVKM_SUBDEV_NR; i++) { + if (!nvkm_device_subdev(dev, order[i].type, order[i].inst)) + continue; + + nvkm_mask(dev, 0x20200 + order[i].offset, 0x00ff, 0x0045); + } +} + +void +gk104_clkgate_fini(struct nvkm_therm *base, bool suspend) +{ + struct gk104_therm *therm = gk104_therm(base); + struct nvkm_device *dev = therm->base.subdev.device; + const struct gk104_clkgate_engine_info *order = therm->clkgate_order; + int i; + + /* ENG_CLK = AUTO->RUN, ENG_PWR = RUN->AUTO */ + for (i = 0; order[i].type != NVKM_SUBDEV_NR; i++) { + if (!nvkm_device_subdev(dev, order[i].type, order[i].inst)) + continue; + + nvkm_mask(dev, 0x20200 + order[i].offset, 0xff, 0x54); + } +} + +const struct gk104_clkgate_engine_info gk104_clkgate_engine_info[] = { + { NVKM_ENGINE_GR, 0, 0x00 }, + { NVKM_ENGINE_MSPDEC, 0, 0x04 }, + { NVKM_ENGINE_MSPPP, 0, 0x08 }, + { NVKM_ENGINE_MSVLD, 0, 0x0c }, + { NVKM_ENGINE_CE, 0, 0x10 }, + { NVKM_ENGINE_CE, 1, 0x14 }, + { NVKM_ENGINE_MSENC, 0, 0x18 }, + { NVKM_ENGINE_CE, 2, 0x1c }, + { NVKM_SUBDEV_NR }, +}; + +const struct gf100_idle_filter gk104_idle_filter = { + .fecs = 0x00001000, + .hubmmu = 0x00001000, +}; + +static const struct nvkm_therm_func +gk104_therm_func = { + .init = gf119_therm_init, + .fini = g84_therm_fini, + .pwm_ctrl = gf119_fan_pwm_ctrl, + .pwm_get = gf119_fan_pwm_get, + .pwm_set = gf119_fan_pwm_set, + .pwm_clock = gf119_fan_pwm_clock, + .temp_get = g84_temp_get, + .fan_sense = gt215_therm_fan_sense, + .program_alarms = nvkm_therm_program_alarms_polling, + .clkgate_init = gf100_clkgate_init, + .clkgate_enable = gk104_clkgate_enable, + .clkgate_fini = gk104_clkgate_fini, +}; + +static int +gk104_therm_new_(const struct nvkm_therm_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, + const struct gk104_clkgate_engine_info *clkgate_order, + const struct gf100_idle_filter *idle_filter, + struct nvkm_therm **ptherm) +{ + struct gk104_therm *therm = kzalloc(sizeof(*therm), GFP_KERNEL); + + if (!therm) + return -ENOMEM; + + nvkm_therm_ctor(&therm->base, device, type, inst, func); + *ptherm = &therm->base; + therm->clkgate_order = clkgate_order; + therm->idle_filter = idle_filter; + return 0; +} + +int +gk104_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_therm **ptherm) +{ + return gk104_therm_new_(&gk104_therm_func, device, type, inst, + gk104_clkgate_engine_info, &gk104_idle_filter, + ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.h b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.h new file mode 100644 index 000000000..9a8641421 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gk104.h @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Lyude Paul + */ + +#ifndef __GK104_THERM_H__ +#define __GK104_THERM_H__ +#define gk104_therm(p) (container_of((p), struct gk104_therm, base)) + +#include +#include "priv.h" +#include "gf100.h" + +struct gk104_clkgate_engine_info { + enum nvkm_subdev_type type; + int inst; + u8 offset; +}; + +struct gk104_therm { + struct nvkm_therm base; + + const struct gk104_clkgate_engine_info *clkgate_order; + const struct gf100_idle_filter *idle_filter; +}; + +extern const struct gk104_clkgate_engine_info gk104_clkgate_engine_info[]; +extern const struct gf100_idle_filter gk104_idle_filter; + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c new file mode 100644 index 000000000..c845fd392 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c @@ -0,0 +1,75 @@ +/* + * Copyright 2014 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +static int +gm107_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable) +{ + /* nothing to do, it seems hardwired */ + return 0; +} + +static int +gm107_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty) +{ + struct nvkm_device *device = therm->subdev.device; + *divs = nvkm_rd32(device, 0x10eb20) & 0x1fff; + *duty = nvkm_rd32(device, 0x10eb24) & 0x1fff; + return 0; +} + +static int +gm107_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty) +{ + struct nvkm_device *device = therm->subdev.device; + nvkm_mask(device, 0x10eb10, 0x1fff, divs); /* keep the high bits */ + nvkm_wr32(device, 0x10eb14, duty | 0x80000000); + return 0; +} + +static int +gm107_fan_pwm_clock(struct nvkm_therm *therm, int line) +{ + return therm->subdev.device->crystal * 1000; +} + +static const struct nvkm_therm_func +gm107_therm = { + .init = gf119_therm_init, + .fini = g84_therm_fini, + .pwm_ctrl = gm107_fan_pwm_ctrl, + .pwm_get = gm107_fan_pwm_get, + .pwm_set = gm107_fan_pwm_set, + .pwm_clock = gm107_fan_pwm_clock, + .temp_get = g84_temp_get, + .fan_sense = gt215_therm_fan_sense, + .program_alarms = nvkm_therm_program_alarms_polling, +}; + +int +gm107_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + return nvkm_therm_new_(&gm107_therm, device, type, inst, ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm200.c new file mode 100644 index 000000000..e0cdd1246 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm200.c @@ -0,0 +1,39 @@ +/* + * Copyright 2017 Karol Herbst + * + * 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: Karol Herbst + */ +#include "priv.h" + +static const struct nvkm_therm_func +gm200_therm = { + .init = g84_therm_init, + .fini = g84_therm_fini, + .temp_get = g84_temp_get, + .program_alarms = nvkm_therm_program_alarms_polling, +}; + +int +gm200_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + return nvkm_therm_new_(&gm200_therm, device, type, inst, ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gp100.c new file mode 100644 index 000000000..44f021392 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gp100.c @@ -0,0 +1,56 @@ +/* + * Copyright 2017 Rhys Kidd + * + * 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: Rhys Kidd + */ +#include "priv.h" + +static int +gp100_temp_get(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + struct nvkm_subdev *subdev = &therm->subdev; + u32 tsensor = nvkm_rd32(device, 0x020460); + u32 inttemp = (tsensor & 0x0001fff8); + + /* device SHADOWed */ + if (tsensor & 0x40000000) + nvkm_trace(subdev, "reading temperature from SHADOWed sensor\n"); + + /* device valid */ + if (tsensor & 0x20000000) + return (inttemp >> 8); + else + return -ENODEV; +} + +static const struct nvkm_therm_func +gp100_therm = { + .temp_get = gp100_temp_get, + .program_alarms = nvkm_therm_program_alarms_polling, +}; + +int +gp100_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + return nvkm_therm_new_(&gp100_therm, device, type, inst, ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c new file mode 100644 index 000000000..9e451bd93 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c @@ -0,0 +1,75 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include + +int +gt215_therm_fan_sense(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + u32 tach = nvkm_rd32(device, 0x00e728) & 0x0000ffff; + u32 ctrl = nvkm_rd32(device, 0x00e720); + if (ctrl & 0x00000001) + return tach * 60 / 2; + return -ENODEV; +} + +static void +gt215_therm_init(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + struct dcb_gpio_func *tach = &therm->fan->tach; + + g84_sensor_setup(therm); + + /* enable fan tach, count revolutions per-second */ + nvkm_mask(device, 0x00e720, 0x00000003, 0x00000002); + if (tach->func != DCB_GPIO_UNUSED) { + nvkm_wr32(device, 0x00e724, device->crystal * 1000); + nvkm_mask(device, 0x00e720, 0x001f0000, tach->line << 16); + nvkm_mask(device, 0x00e720, 0x00000001, 0x00000001); + } + nvkm_mask(device, 0x00e720, 0x00000002, 0x00000000); +} + +static const struct nvkm_therm_func +gt215_therm = { + .init = gt215_therm_init, + .fini = g84_therm_fini, + .pwm_ctrl = nv50_fan_pwm_ctrl, + .pwm_get = nv50_fan_pwm_get, + .pwm_set = nv50_fan_pwm_set, + .pwm_clock = nv50_fan_pwm_clock, + .temp_get = g84_temp_get, + .fan_sense = gt215_therm_fan_sense, + .program_alarms = nvkm_therm_program_alarms_polling, +}; + +int +gt215_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + return nvkm_therm_new_(>215_therm, device, type, inst, ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c new file mode 100644 index 000000000..abf3eda68 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c @@ -0,0 +1,127 @@ +/* + * Copyright 2012 Nouveau community + * + * 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: Martin Peres + */ +#include "priv.h" + +#include +#include + +static bool +probe_monitoring_device(struct nvkm_i2c_bus *bus, + struct i2c_board_info *info, void *data) +{ + struct nvkm_therm *therm = data; + struct nvbios_therm_sensor *sensor = &therm->bios_sensor; + struct i2c_client *client; + + request_module("%s%s", I2C_MODULE_PREFIX, info->type); + + client = i2c_new_client_device(&bus->i2c, info); + if (IS_ERR(client)) + return false; + + if (!client->dev.driver || + to_i2c_driver(client->dev.driver)->detect(client, info)) { + i2c_unregister_device(client); + return false; + } + + nvkm_debug(&therm->subdev, + "Found an %s at address 0x%x (controlled by lm_sensors, " + "temp offset %+i C)\n", + info->type, info->addr, sensor->offset_constant); + therm->ic = client; + return true; +} + +static struct nvkm_i2c_bus_probe +nv_board_infos[] = { + { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, 0 }, + { { I2C_BOARD_INFO("w83781d", 0x2d) }, 0 }, + { { I2C_BOARD_INFO("adt7473", 0x2e) }, 40 }, + { { I2C_BOARD_INFO("adt7473", 0x2d) }, 40 }, + { { I2C_BOARD_INFO("adt7473", 0x2c) }, 40 }, + { { I2C_BOARD_INFO("f75375", 0x2e) }, 0 }, + { { I2C_BOARD_INFO("lm99", 0x4c) }, 0 }, + { { I2C_BOARD_INFO("lm90", 0x4c) }, 0 }, + { { I2C_BOARD_INFO("lm90", 0x4d) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x18) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x19) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x1a) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x29) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x2a) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x2b) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x4c) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x4d) }, 0 }, + { { I2C_BOARD_INFO("adm1021", 0x4e) }, 0 }, + { { I2C_BOARD_INFO("lm63", 0x18) }, 0 }, + { { I2C_BOARD_INFO("lm63", 0x4e) }, 0 }, + { } +}; + +void +nvkm_therm_ic_ctor(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvkm_i2c *i2c = device->i2c; + struct nvkm_i2c_bus *bus; + struct nvbios_extdev_func extdev_entry; + + bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_PRI); + if (!bus) + return; + + if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) { + struct nvkm_i2c_bus_probe board[] = { + { { I2C_BOARD_INFO("lm90", extdev_entry.addr >> 1) }, 0}, + { } + }; + + nvkm_i2c_bus_probe(bus, "monitoring device", board, + probe_monitoring_device, therm); + if (therm->ic) + return; + } + + if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_ADT7473, &extdev_entry)) { + struct nvkm_i2c_bus_probe board[] = { + { { I2C_BOARD_INFO("adt7473", extdev_entry.addr >> 1) }, 20 }, + { } + }; + + nvkm_i2c_bus_probe(bus, "monitoring device", board, + probe_monitoring_device, therm); + if (therm->ic) + return; + } + + if (nvbios_extdev_skip_probe(bios)) + return; + + /* The vbios doesn't provide the address of an exisiting monitoring + device. Let's try our static list. + */ + nvkm_i2c_bus_probe(bus, "monitoring device", nv_board_infos, + probe_monitoring_device, therm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c new file mode 100644 index 000000000..c13fee973 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c @@ -0,0 +1,204 @@ +/* + * 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 + * Martin Peres + */ +#include "priv.h" + +enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 }; + +static enum nv40_sensor_style +nv40_sensor_style(struct nvkm_therm *therm) +{ + switch (therm->subdev.device->chipset) { + case 0x43: + case 0x44: + case 0x4a: + case 0x47: + return OLD_STYLE; + case 0x46: + case 0x49: + case 0x4b: + case 0x4e: + case 0x4c: + case 0x67: + case 0x68: + case 0x63: + return NEW_STYLE; + default: + return INVALID_STYLE; + } +} + +static int +nv40_sensor_setup(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + enum nv40_sensor_style style = nv40_sensor_style(therm); + + /* enable ADC readout and disable the ALARM threshold */ + if (style == NEW_STYLE) { + nvkm_mask(device, 0x15b8, 0x80000000, 0); + nvkm_wr32(device, 0x15b0, 0x80003fff); + mdelay(20); /* wait for the temperature to stabilize */ + return nvkm_rd32(device, 0x15b4) & 0x3fff; + } else if (style == OLD_STYLE) { + nvkm_wr32(device, 0x15b0, 0xff); + mdelay(20); /* wait for the temperature to stabilize */ + return nvkm_rd32(device, 0x15b4) & 0xff; + } else + return -ENODEV; +} + +static int +nv40_temp_get(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + struct nvbios_therm_sensor *sensor = &therm->bios_sensor; + enum nv40_sensor_style style = nv40_sensor_style(therm); + int core_temp; + + if (style == NEW_STYLE) { + nvkm_wr32(device, 0x15b0, 0x80003fff); + core_temp = nvkm_rd32(device, 0x15b4) & 0x3fff; + } else if (style == OLD_STYLE) { + nvkm_wr32(device, 0x15b0, 0xff); + core_temp = nvkm_rd32(device, 0x15b4) & 0xff; + } else + return -ENODEV; + + /* if the slope or the offset is unset, do no use the sensor */ + if (!sensor->slope_div || !sensor->slope_mult || + !sensor->offset_num || !sensor->offset_den) + return -ENODEV; + + core_temp = core_temp * sensor->slope_mult / sensor->slope_div; + core_temp = core_temp + sensor->offset_num / sensor->offset_den; + core_temp = core_temp + sensor->offset_constant - 8; + + /* reserve negative temperatures for errors */ + if (core_temp < 0) + core_temp = 0; + + return core_temp; +} + +static int +nv40_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + u32 mask = enable ? 0x80000000 : 0x00000000; + if (line == 2) nvkm_mask(device, 0x0010f0, 0x80000000, mask); + else if (line == 9) nvkm_mask(device, 0x0015f4, 0x80000000, mask); + else { + nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line); + return -ENODEV; + } + return 0; +} + +static int +nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + if (line == 2) { + u32 reg = nvkm_rd32(device, 0x0010f0); + if (reg & 0x80000000) { + *duty = (reg & 0x7fff0000) >> 16; + *divs = (reg & 0x00007fff); + return 0; + } + } else + if (line == 9) { + u32 reg = nvkm_rd32(device, 0x0015f4); + if (reg & 0x80000000) { + *divs = nvkm_rd32(device, 0x0015f8); + *duty = (reg & 0x7fffffff); + return 0; + } + } else { + nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line); + return -ENODEV; + } + + return -EINVAL; +} + +static int +nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + if (line == 2) { + nvkm_mask(device, 0x0010f0, 0x7fff7fff, (duty << 16) | divs); + } else + if (line == 9) { + nvkm_wr32(device, 0x0015f8, divs); + nvkm_mask(device, 0x0015f4, 0x7fffffff, duty); + } else { + nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", line); + return -ENODEV; + } + + return 0; +} + +void +nv40_therm_intr(struct nvkm_therm *therm) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_device *device = subdev->device; + uint32_t stat = nvkm_rd32(device, 0x1100); + + /* traitement */ + + /* ack all IRQs */ + nvkm_wr32(device, 0x1100, 0x70000); + + nvkm_error(subdev, "THERM received an IRQ: stat = %x\n", stat); +} + +static void +nv40_therm_init(struct nvkm_therm *therm) +{ + nv40_sensor_setup(therm); +} + +static const struct nvkm_therm_func +nv40_therm = { + .init = nv40_therm_init, + .intr = nv40_therm_intr, + .pwm_ctrl = nv40_fan_pwm_ctrl, + .pwm_get = nv40_fan_pwm_get, + .pwm_set = nv40_fan_pwm_set, + .temp_get = nv40_temp_get, + .program_alarms = nvkm_therm_program_alarms_polling, +}; + +int +nv40_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + return nvkm_therm_new_(&nv40_therm, device, type, inst, ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c new file mode 100644 index 000000000..9cf16a75a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c @@ -0,0 +1,176 @@ +/* + * 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 + * Martin Peres + */ +#include "priv.h" + +static int +pwm_info(struct nvkm_therm *therm, int *line, int *ctrl, int *indx) +{ + struct nvkm_subdev *subdev = &therm->subdev; + + if (*line == 0x04) { + *ctrl = 0x00e100; + *line = 4; + *indx = 0; + } else + if (*line == 0x09) { + *ctrl = 0x00e100; + *line = 9; + *indx = 1; + } else + if (*line == 0x10) { + *ctrl = 0x00e28c; + *line = 0; + *indx = 0; + } else { + nvkm_error(subdev, "unknown pwm ctrl for gpio %d\n", *line); + return -ENODEV; + } + + return 0; +} + +int +nv50_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable) +{ + struct nvkm_device *device = therm->subdev.device; + u32 data = enable ? 0x00000001 : 0x00000000; + int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); + if (ret == 0) + nvkm_mask(device, ctrl, 0x00010001 << line, data << line); + return ret; +} + +int +nv50_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty) +{ + struct nvkm_device *device = therm->subdev.device; + int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); + if (ret) + return ret; + + if (nvkm_rd32(device, ctrl) & (1 << line)) { + *divs = nvkm_rd32(device, 0x00e114 + (id * 8)); + *duty = nvkm_rd32(device, 0x00e118 + (id * 8)); + return 0; + } + + return -EINVAL; +} + +int +nv50_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty) +{ + struct nvkm_device *device = therm->subdev.device; + int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); + if (ret) + return ret; + + nvkm_wr32(device, 0x00e114 + (id * 8), divs); + nvkm_wr32(device, 0x00e118 + (id * 8), duty | 0x80000000); + return 0; +} + +int +nv50_fan_pwm_clock(struct nvkm_therm *therm, int line) +{ + struct nvkm_device *device = therm->subdev.device; + int pwm_clock; + + /* determine the PWM source clock */ + if (device->chipset > 0x50 && device->chipset < 0x94) { + u8 pwm_div = nvkm_rd32(device, 0x410c); + if (nvkm_rd32(device, 0xc040) & 0x800000) { + /* Use the HOST clock (100 MHz) + * Where does this constant(2.4) comes from? */ + pwm_clock = (100000000 >> pwm_div) * 10 / 24; + } else { + /* Where does this constant(20) comes from? */ + pwm_clock = (device->crystal * 1000) >> pwm_div; + pwm_clock /= 20; + } + } else { + pwm_clock = (device->crystal * 1000) / 20; + } + + return pwm_clock; +} + +static void +nv50_sensor_setup(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + nvkm_mask(device, 0x20010, 0x40000000, 0x0); + mdelay(20); /* wait for the temperature to stabilize */ +} + +static int +nv50_temp_get(struct nvkm_therm *therm) +{ + struct nvkm_device *device = therm->subdev.device; + struct nvbios_therm_sensor *sensor = &therm->bios_sensor; + int core_temp; + + core_temp = nvkm_rd32(device, 0x20014) & 0x3fff; + + /* if the slope or the offset is unset, do no use the sensor */ + if (!sensor->slope_div || !sensor->slope_mult || + !sensor->offset_num || !sensor->offset_den) + return -ENODEV; + + core_temp = core_temp * sensor->slope_mult / sensor->slope_div; + core_temp = core_temp + sensor->offset_num / sensor->offset_den; + core_temp = core_temp + sensor->offset_constant - 8; + + /* reserve negative temperatures for errors */ + if (core_temp < 0) + core_temp = 0; + + return core_temp; +} + +static void +nv50_therm_init(struct nvkm_therm *therm) +{ + nv50_sensor_setup(therm); +} + +static const struct nvkm_therm_func +nv50_therm = { + .init = nv50_therm_init, + .intr = nv40_therm_intr, + .pwm_ctrl = nv50_fan_pwm_ctrl, + .pwm_get = nv50_fan_pwm_get, + .pwm_set = nv50_fan_pwm_set, + .pwm_clock = nv50_fan_pwm_clock, + .temp_get = nv50_temp_get, + .program_alarms = nvkm_therm_program_alarms_polling, +}; + +int +nv50_therm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_therm **ptherm) +{ + return nvkm_therm_new_(&nv50_therm, device, type, inst, ptherm); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h new file mode 100644 index 000000000..54e960589 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h @@ -0,0 +1,137 @@ +#ifndef __NVTHERM_PRIV_H__ +#define __NVTHERM_PRIV_H__ +#define nvkm_therm(p) container_of((p), struct nvkm_therm, subdev) +/* + * Copyright 2012 The Nouveau community + * + * 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: Martin Peres + */ +#include +#include +#include +#include +#include + +int nvkm_therm_new_(const struct nvkm_therm_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_therm **); +void nvkm_therm_ctor(struct nvkm_therm *, struct nvkm_device *, enum nvkm_subdev_type, int, + const struct nvkm_therm_func *); + +struct nvkm_fan { + struct nvkm_therm *parent; + const char *type; + + struct nvbios_therm_fan bios; + struct nvbios_perf_fan perf; + + struct nvkm_alarm alarm; + spinlock_t lock; + int percent; + + int (*get)(struct nvkm_therm *); + int (*set)(struct nvkm_therm *, int percent); + + struct dcb_gpio_func tach; +}; + +int nvkm_therm_fan_mode(struct nvkm_therm *, int mode); +int nvkm_therm_attr_get(struct nvkm_therm *, enum nvkm_therm_attr_type); +int nvkm_therm_attr_set(struct nvkm_therm *, enum nvkm_therm_attr_type, int); + +void nvkm_therm_ic_ctor(struct nvkm_therm *); + +int nvkm_therm_sensor_ctor(struct nvkm_therm *); + +int nvkm_therm_fan_ctor(struct nvkm_therm *); +int nvkm_therm_fan_init(struct nvkm_therm *); +int nvkm_therm_fan_fini(struct nvkm_therm *, bool suspend); +int nvkm_therm_fan_get(struct nvkm_therm *); +int nvkm_therm_fan_set(struct nvkm_therm *, bool now, int percent); +int nvkm_therm_fan_user_get(struct nvkm_therm *); +int nvkm_therm_fan_user_set(struct nvkm_therm *, int percent); + +int nvkm_therm_sensor_init(struct nvkm_therm *); +int nvkm_therm_sensor_fini(struct nvkm_therm *, bool suspend); +void nvkm_therm_sensor_preinit(struct nvkm_therm *); +void nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *, + enum nvkm_therm_thrs, + enum nvkm_therm_thrs_state); +enum nvkm_therm_thrs_state +nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *, + enum nvkm_therm_thrs); +void nvkm_therm_sensor_event(struct nvkm_therm *, enum nvkm_therm_thrs, + enum nvkm_therm_thrs_direction); +void nvkm_therm_program_alarms_polling(struct nvkm_therm *); + +struct nvkm_therm_func { + void (*init)(struct nvkm_therm *); + void (*fini)(struct nvkm_therm *); + void (*intr)(struct nvkm_therm *); + + int (*pwm_ctrl)(struct nvkm_therm *, int line, bool); + int (*pwm_get)(struct nvkm_therm *, int line, u32 *, u32 *); + int (*pwm_set)(struct nvkm_therm *, int line, u32, u32); + int (*pwm_clock)(struct nvkm_therm *, int line); + + int (*temp_get)(struct nvkm_therm *); + + int (*fan_sense)(struct nvkm_therm *); + + void (*program_alarms)(struct nvkm_therm *); + + void (*clkgate_init)(struct nvkm_therm *, + const struct nvkm_therm_clkgate_pack *); + void (*clkgate_enable)(struct nvkm_therm *); + void (*clkgate_fini)(struct nvkm_therm *, bool); +}; + +void nv40_therm_intr(struct nvkm_therm *); + +int nv50_fan_pwm_ctrl(struct nvkm_therm *, int, bool); +int nv50_fan_pwm_get(struct nvkm_therm *, int, u32 *, u32 *); +int nv50_fan_pwm_set(struct nvkm_therm *, int, u32, u32); +int nv50_fan_pwm_clock(struct nvkm_therm *, int); + +int g84_temp_get(struct nvkm_therm *); +void g84_sensor_setup(struct nvkm_therm *); +void g84_therm_fini(struct nvkm_therm *); + +int gt215_therm_fan_sense(struct nvkm_therm *); + +void gf100_clkgate_init(struct nvkm_therm *, + const struct nvkm_therm_clkgate_pack *); + +void g84_therm_init(struct nvkm_therm *); + +int gf119_fan_pwm_ctrl(struct nvkm_therm *, int, bool); +int gf119_fan_pwm_get(struct nvkm_therm *, int, u32 *, u32 *); +int gf119_fan_pwm_set(struct nvkm_therm *, int, u32, u32); +int gf119_fan_pwm_clock(struct nvkm_therm *, int); +void gf119_therm_init(struct nvkm_therm *); + +void gk104_therm_init(struct nvkm_therm *); +void gk104_clkgate_enable(struct nvkm_therm *); +void gk104_clkgate_fini(struct nvkm_therm *, bool); + +int nvkm_fanpwm_create(struct nvkm_therm *, struct dcb_gpio_func *); +int nvkm_fantog_create(struct nvkm_therm *, struct dcb_gpio_func *); +int nvkm_fannil_create(struct nvkm_therm *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c new file mode 100644 index 000000000..ddb2b2c60 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c @@ -0,0 +1,253 @@ +/* + * Copyright 2012 The Nouveau community + * + * 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: Martin Peres + */ +#include "priv.h" + +static void +nvkm_therm_temp_set_defaults(struct nvkm_therm *therm) +{ + therm->bios_sensor.offset_constant = 0; + + therm->bios_sensor.thrs_fan_boost.temp = 90; + therm->bios_sensor.thrs_fan_boost.hysteresis = 3; + + therm->bios_sensor.thrs_down_clock.temp = 95; + therm->bios_sensor.thrs_down_clock.hysteresis = 3; + + therm->bios_sensor.thrs_critical.temp = 105; + therm->bios_sensor.thrs_critical.hysteresis = 5; + + therm->bios_sensor.thrs_shutdown.temp = 135; + therm->bios_sensor.thrs_shutdown.hysteresis = 5; /*not that it matters */ +} + +static void +nvkm_therm_temp_safety_checks(struct nvkm_therm *therm) +{ + struct nvbios_therm_sensor *s = &therm->bios_sensor; + + /* enforce a minimum hysteresis on thresholds */ + s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2); + s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2); + s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2); + s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2); +} + +/* must be called with alarm_program_lock taken ! */ +void +nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *therm, + enum nvkm_therm_thrs thrs, + enum nvkm_therm_thrs_state st) +{ + therm->sensor.alarm_state[thrs] = st; +} + +/* must be called with alarm_program_lock taken ! */ +enum nvkm_therm_thrs_state +nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *therm, + enum nvkm_therm_thrs thrs) +{ + return therm->sensor.alarm_state[thrs]; +} + +static void +nv_poweroff_work(struct work_struct *work) +{ + orderly_poweroff(true); + kfree(work); +} + +void +nvkm_therm_sensor_event(struct nvkm_therm *therm, enum nvkm_therm_thrs thrs, + enum nvkm_therm_thrs_direction dir) +{ + struct nvkm_subdev *subdev = &therm->subdev; + bool active; + static const char * const thresholds[] = { + "fanboost", "downclock", "critical", "shutdown" + }; + int temperature = therm->func->temp_get(therm); + + if (thrs < 0 || thrs > 3) + return; + + if (dir == NVKM_THERM_THRS_FALLING) + nvkm_info(subdev, + "temperature (%i C) went below the '%s' threshold\n", + temperature, thresholds[thrs]); + else + nvkm_info(subdev, "temperature (%i C) hit the '%s' threshold\n", + temperature, thresholds[thrs]); + + active = (dir == NVKM_THERM_THRS_RISING); + switch (thrs) { + case NVKM_THERM_THRS_FANBOOST: + if (active) { + nvkm_therm_fan_set(therm, true, 100); + nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO); + } + break; + case NVKM_THERM_THRS_DOWNCLOCK: + if (therm->emergency.downclock) + therm->emergency.downclock(therm, active); + break; + case NVKM_THERM_THRS_CRITICAL: + if (therm->emergency.pause) + therm->emergency.pause(therm, active); + break; + case NVKM_THERM_THRS_SHUTDOWN: + if (active) { + struct work_struct *work; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (work) { + INIT_WORK(work, nv_poweroff_work); + schedule_work(work); + } + } + break; + case NVKM_THERM_THRS_NR: + break; + } + +} + +/* must be called with alarm_program_lock taken ! */ +static void +nvkm_therm_threshold_hyst_polling(struct nvkm_therm *therm, + const struct nvbios_therm_threshold *thrs, + enum nvkm_therm_thrs thrs_name) +{ + enum nvkm_therm_thrs_direction direction; + enum nvkm_therm_thrs_state prev_state, new_state; + int temp = therm->func->temp_get(therm); + + prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name); + + if (temp >= thrs->temp && prev_state == NVKM_THERM_THRS_LOWER) { + direction = NVKM_THERM_THRS_RISING; + new_state = NVKM_THERM_THRS_HIGHER; + } else if (temp <= thrs->temp - thrs->hysteresis && + prev_state == NVKM_THERM_THRS_HIGHER) { + direction = NVKM_THERM_THRS_FALLING; + new_state = NVKM_THERM_THRS_LOWER; + } else + return; /* nothing to do */ + + nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state); + nvkm_therm_sensor_event(therm, thrs_name, direction); +} + +static void +alarm_timer_callback(struct nvkm_alarm *alarm) +{ + struct nvkm_therm *therm = + container_of(alarm, struct nvkm_therm, sensor.therm_poll_alarm); + struct nvbios_therm_sensor *sensor = &therm->bios_sensor; + struct nvkm_timer *tmr = therm->subdev.device->timer; + unsigned long flags; + + spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags); + + nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost, + NVKM_THERM_THRS_FANBOOST); + + nvkm_therm_threshold_hyst_polling(therm, + &sensor->thrs_down_clock, + NVKM_THERM_THRS_DOWNCLOCK); + + nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_critical, + NVKM_THERM_THRS_CRITICAL); + + nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown, + NVKM_THERM_THRS_SHUTDOWN); + + spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags); + + /* schedule the next poll in one second */ + if (therm->func->temp_get(therm) >= 0) + nvkm_timer_alarm(tmr, 1000000000ULL, alarm); +} + +void +nvkm_therm_program_alarms_polling(struct nvkm_therm *therm) +{ + struct nvbios_therm_sensor *sensor = &therm->bios_sensor; + + nvkm_debug(&therm->subdev, + "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n", + sensor->thrs_fan_boost.temp, + sensor->thrs_fan_boost.hysteresis, + sensor->thrs_down_clock.temp, + sensor->thrs_down_clock.hysteresis, + sensor->thrs_critical.temp, + sensor->thrs_critical.hysteresis, + sensor->thrs_shutdown.temp, + sensor->thrs_shutdown.hysteresis); + + alarm_timer_callback(&therm->sensor.therm_poll_alarm); +} + +int +nvkm_therm_sensor_init(struct nvkm_therm *therm) +{ + therm->func->program_alarms(therm); + return 0; +} + +int +nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend) +{ + struct nvkm_timer *tmr = therm->subdev.device->timer; + if (suspend) + nvkm_timer_alarm(tmr, 0, &therm->sensor.therm_poll_alarm); + return 0; +} + +void +nvkm_therm_sensor_preinit(struct nvkm_therm *therm) +{ + const char *sensor_avail = "yes"; + + if (therm->func->temp_get(therm) < 0) + sensor_avail = "no"; + + nvkm_debug(&therm->subdev, "internal sensor: %s\n", sensor_avail); +} + +int +nvkm_therm_sensor_ctor(struct nvkm_therm *therm) +{ + struct nvkm_subdev *subdev = &therm->subdev; + struct nvkm_bios *bios = subdev->device->bios; + + nvkm_alarm_init(&therm->sensor.therm_poll_alarm, alarm_timer_callback); + + nvkm_therm_temp_set_defaults(therm); + if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE, + &therm->bios_sensor)) + nvkm_error(subdev, "nvbios_therm_sensor_parse failed\n"); + nvkm_therm_temp_safety_checks(therm); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild new file mode 100644 index 000000000..f710da442 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/timer/base.o +nvkm-y += nvkm/subdev/timer/nv04.o +nvkm-y += nvkm/subdev/timer/nv40.o +nvkm-y += nvkm/subdev/timer/nv41.o +nvkm-y += nvkm/subdev/timer/gk20a.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c new file mode 100644 index 000000000..8b0da0c06 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c @@ -0,0 +1,198 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +s64 +nvkm_timer_wait_test(struct nvkm_timer_wait *wait) +{ + struct nvkm_subdev *subdev = &wait->tmr->subdev; + u64 time = nvkm_timer_read(wait->tmr); + + if (wait->reads == 0) { + wait->time0 = time; + wait->time1 = time; + } + + if (wait->time1 == time) { + if (wait->reads++ == 16) { + nvkm_fatal(subdev, "stalled at %016llx\n", time); + return -ETIMEDOUT; + } + } else { + wait->time1 = time; + wait->reads = 1; + } + + if (wait->time1 - wait->time0 > wait->limit) + return -ETIMEDOUT; + + return wait->time1 - wait->time0; +} + +void +nvkm_timer_wait_init(struct nvkm_device *device, u64 nsec, + struct nvkm_timer_wait *wait) +{ + wait->tmr = device->timer; + wait->limit = nsec; + wait->reads = 0; +} + +u64 +nvkm_timer_read(struct nvkm_timer *tmr) +{ + return tmr->func->read(tmr); +} + +void +nvkm_timer_alarm_trigger(struct nvkm_timer *tmr) +{ + struct nvkm_alarm *alarm, *atemp; + unsigned long flags; + LIST_HEAD(exec); + + /* Process pending alarms. */ + spin_lock_irqsave(&tmr->lock, flags); + list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) { + /* Have we hit the earliest alarm that hasn't gone off? */ + if (alarm->timestamp > nvkm_timer_read(tmr)) { + /* Schedule it. If we didn't race, we're done. */ + tmr->func->alarm_init(tmr, alarm->timestamp); + if (alarm->timestamp > nvkm_timer_read(tmr)) + break; + } + + /* Move to completed list. We'll drop the lock before + * executing the callback so it can reschedule itself. + */ + list_del_init(&alarm->head); + list_add(&alarm->exec, &exec); + } + + /* Shut down interrupt if no more pending alarms. */ + if (list_empty(&tmr->alarms)) + tmr->func->alarm_fini(tmr); + spin_unlock_irqrestore(&tmr->lock, flags); + + /* Execute completed callbacks. */ + list_for_each_entry_safe(alarm, atemp, &exec, exec) { + list_del(&alarm->exec); + alarm->func(alarm); + } +} + +void +nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm) +{ + struct nvkm_alarm *list; + unsigned long flags; + + /* Remove alarm from pending list. + * + * This both protects against the corruption of the list, + * and implements alarm rescheduling/cancellation. + */ + spin_lock_irqsave(&tmr->lock, flags); + list_del_init(&alarm->head); + + if (nsec) { + /* Insert into pending list, ordered earliest to latest. */ + alarm->timestamp = nvkm_timer_read(tmr) + nsec; + list_for_each_entry(list, &tmr->alarms, head) { + if (list->timestamp > alarm->timestamp) + break; + } + + list_add_tail(&alarm->head, &list->head); + + /* Update HW if this is now the earliest alarm. */ + list = list_first_entry(&tmr->alarms, typeof(*list), head); + if (list == alarm) { + tmr->func->alarm_init(tmr, alarm->timestamp); + /* This shouldn't happen if callers aren't stupid. + * + * Worst case scenario is that it'll take roughly + * 4 seconds for the next alarm to trigger. + */ + WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr)); + } + } + spin_unlock_irqrestore(&tmr->lock, flags); +} + +static void +nvkm_timer_intr(struct nvkm_subdev *subdev) +{ + struct nvkm_timer *tmr = nvkm_timer(subdev); + tmr->func->intr(tmr); +} + +static int +nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_timer *tmr = nvkm_timer(subdev); + tmr->func->alarm_fini(tmr); + return 0; +} + +static int +nvkm_timer_init(struct nvkm_subdev *subdev) +{ + struct nvkm_timer *tmr = nvkm_timer(subdev); + if (tmr->func->init) + tmr->func->init(tmr); + tmr->func->time(tmr, ktime_to_ns(ktime_get())); + nvkm_timer_alarm_trigger(tmr); + return 0; +} + +static void * +nvkm_timer_dtor(struct nvkm_subdev *subdev) +{ + return nvkm_timer(subdev); +} + +static const struct nvkm_subdev_func +nvkm_timer = { + .dtor = nvkm_timer_dtor, + .init = nvkm_timer_init, + .fini = nvkm_timer_fini, + .intr = nvkm_timer_intr, +}; + +int +nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_timer **ptmr) +{ + struct nvkm_timer *tmr; + + if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL))) + return -ENOMEM; + + nvkm_subdev_ctor(&nvkm_timer, device, type, inst, &tmr->subdev); + tmr->func = func; + INIT_LIST_HEAD(&tmr->alarms); + spin_lock_init(&tmr->lock); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c new file mode 100644 index 000000000..73c3776b6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c @@ -0,0 +1,40 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_timer_func +gk20a_timer = { + .intr = nv04_timer_intr, + .read = nv04_timer_read, + .time = nv04_timer_time, + .alarm_init = nv04_timer_alarm_init, + .alarm_fini = nv04_timer_alarm_fini, +}; + +int +gk20a_timer_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_timer **ptmr) +{ + return nvkm_timer_new_(&gk20a_timer, device, type, inst, ptmr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c new file mode 100644 index 000000000..0058e856b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c @@ -0,0 +1,152 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "regsnv04.h" + +void +nv04_timer_time(struct nvkm_timer *tmr, u64 time) +{ + struct nvkm_subdev *subdev = &tmr->subdev; + struct nvkm_device *device = subdev->device; + u32 hi = upper_32_bits(time); + u32 lo = lower_32_bits(time); + + nvkm_debug(subdev, "time low : %08x\n", lo); + nvkm_debug(subdev, "time high : %08x\n", hi); + + nvkm_wr32(device, NV04_PTIMER_TIME_1, hi); + nvkm_wr32(device, NV04_PTIMER_TIME_0, lo); +} + +u64 +nv04_timer_read(struct nvkm_timer *tmr) +{ + struct nvkm_device *device = tmr->subdev.device; + u32 hi, lo; + + do { + hi = nvkm_rd32(device, NV04_PTIMER_TIME_1); + lo = nvkm_rd32(device, NV04_PTIMER_TIME_0); + } while (hi != nvkm_rd32(device, NV04_PTIMER_TIME_1)); + + return ((u64)hi << 32 | lo); +} + +void +nv04_timer_alarm_fini(struct nvkm_timer *tmr) +{ + struct nvkm_device *device = tmr->subdev.device; + nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000); +} + +void +nv04_timer_alarm_init(struct nvkm_timer *tmr, u32 time) +{ + struct nvkm_device *device = tmr->subdev.device; + nvkm_wr32(device, NV04_PTIMER_ALARM_0, time); + nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000001); +} + +void +nv04_timer_intr(struct nvkm_timer *tmr) +{ + struct nvkm_subdev *subdev = &tmr->subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0); + + if (stat & 0x00000001) { + nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001); + nvkm_timer_alarm_trigger(tmr); + stat &= ~0x00000001; + } + + if (stat) { + nvkm_error(subdev, "intr %08x\n", stat); + nvkm_wr32(device, NV04_PTIMER_INTR_0, stat); + } +} + +static void +nv04_timer_init(struct nvkm_timer *tmr) +{ + struct nvkm_subdev *subdev = &tmr->subdev; + struct nvkm_device *device = subdev->device; + u32 f = 0; /*XXX: nvclk */ + u32 n, d; + + /* aim for 31.25MHz, which gives us nanosecond timestamps */ + d = 1000000 / 32; + n = f; + + if (!f) { + n = nvkm_rd32(device, NV04_PTIMER_NUMERATOR); + d = nvkm_rd32(device, NV04_PTIMER_DENOMINATOR); + if (!n || !d) { + n = 1; + d = 1; + } + nvkm_warn(subdev, "unknown input clock freq\n"); + } + + /* reduce ratio to acceptable values */ + while (((n % 5) == 0) && ((d % 5) == 0)) { + n /= 5; + d /= 5; + } + + while (((n % 2) == 0) && ((d % 2) == 0)) { + n /= 2; + d /= 2; + } + + while (n > 0xffff || d > 0xffff) { + n >>= 1; + d >>= 1; + } + + nvkm_debug(subdev, "input frequency : %dHz\n", f); + nvkm_debug(subdev, "numerator : %08x\n", n); + nvkm_debug(subdev, "denominator : %08x\n", d); + nvkm_debug(subdev, "timer frequency : %dHz\n", f * d / n); + + nvkm_wr32(device, NV04_PTIMER_NUMERATOR, n); + nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, d); +} + +static const struct nvkm_timer_func +nv04_timer = { + .init = nv04_timer_init, + .intr = nv04_timer_intr, + .read = nv04_timer_read, + .time = nv04_timer_time, + .alarm_init = nv04_timer_alarm_init, + .alarm_fini = nv04_timer_alarm_fini, +}; + +int +nv04_timer_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_timer **ptmr) +{ + return nvkm_timer_new_(&nv04_timer, device, type, inst, ptmr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv40.c new file mode 100644 index 000000000..7e1f8c22f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv40.c @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "regsnv04.h" + +static void +nv40_timer_init(struct nvkm_timer *tmr) +{ + struct nvkm_subdev *subdev = &tmr->subdev; + struct nvkm_device *device = subdev->device; + u32 f = 0; /*XXX: figure this out */ + u32 n, d; + + /* aim for 31.25MHz, which gives us nanosecond timestamps */ + d = 1000000 / 32; + n = f; + + if (!f) { + n = nvkm_rd32(device, NV04_PTIMER_NUMERATOR); + d = nvkm_rd32(device, NV04_PTIMER_DENOMINATOR); + if (!n || !d) { + n = 1; + d = 1; + } + nvkm_warn(subdev, "unknown input clock freq\n"); + } + + /* reduce ratio to acceptable values */ + while (((n % 5) == 0) && ((d % 5) == 0)) { + n /= 5; + d /= 5; + } + + while (((n % 2) == 0) && ((d % 2) == 0)) { + n /= 2; + d /= 2; + } + + while (n > 0xffff || d > 0xffff) { + n >>= 1; + d >>= 1; + } + + nvkm_debug(subdev, "input frequency : %dHz\n", f); + nvkm_debug(subdev, "numerator : %08x\n", n); + nvkm_debug(subdev, "denominator : %08x\n", d); + nvkm_debug(subdev, "timer frequency : %dHz\n", f * d / n); + + nvkm_wr32(device, NV04_PTIMER_NUMERATOR, n); + nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, d); +} + +static const struct nvkm_timer_func +nv40_timer = { + .init = nv40_timer_init, + .intr = nv04_timer_intr, + .read = nv04_timer_read, + .time = nv04_timer_time, + .alarm_init = nv04_timer_alarm_init, + .alarm_fini = nv04_timer_alarm_fini, +}; + +int +nv40_timer_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_timer **ptmr) +{ + return nvkm_timer_new_(&nv40_timer, device, type, inst, ptmr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv41.c new file mode 100644 index 000000000..c2b263721 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv41.c @@ -0,0 +1,86 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" +#include "regsnv04.h" + +static void +nv41_timer_init(struct nvkm_timer *tmr) +{ + struct nvkm_subdev *subdev = &tmr->subdev; + struct nvkm_device *device = subdev->device; + u32 f = device->crystal; + u32 m = 1, n, d; + + /* aim for 31.25MHz, which gives us nanosecond timestamps */ + d = 1000000 / 32; + n = f; + + while (n < (d * 2)) { + n += (n / m); + m++; + } + + /* reduce ratio to acceptable values */ + while (((n % 5) == 0) && ((d % 5) == 0)) { + n /= 5; + d /= 5; + } + + while (((n % 2) == 0) && ((d % 2) == 0)) { + n /= 2; + d /= 2; + } + + while (n > 0xffff || d > 0xffff) { + n >>= 1; + d >>= 1; + } + + nvkm_debug(subdev, "input frequency : %dHz\n", f); + nvkm_debug(subdev, "input multiplier: %d\n", m); + nvkm_debug(subdev, "numerator : %08x\n", n); + nvkm_debug(subdev, "denominator : %08x\n", d); + nvkm_debug(subdev, "timer frequency : %dHz\n", (f * m) * d / n); + + nvkm_wr32(device, 0x009220, m - 1); + nvkm_wr32(device, NV04_PTIMER_NUMERATOR, n); + nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, d); +} + +static const struct nvkm_timer_func +nv41_timer = { + .init = nv41_timer_init, + .intr = nv04_timer_intr, + .read = nv04_timer_read, + .time = nv04_timer_time, + .alarm_init = nv04_timer_alarm_init, + .alarm_fini = nv04_timer_alarm_fini, +}; + +int +nv41_timer_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_timer **ptmr) +{ + return nvkm_timer_new_(&nv41_timer, device, type, inst, ptmr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h new file mode 100644 index 000000000..e6debe7e2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_TIMER_PRIV_H__ +#define __NVKM_TIMER_PRIV_H__ +#define nvkm_timer(p) container_of((p), struct nvkm_timer, subdev) +#include + +int nvkm_timer_new_(const struct nvkm_timer_func *, struct nvkm_device *, enum nvkm_subdev_type, + int, struct nvkm_timer **); + +struct nvkm_timer_func { + void (*init)(struct nvkm_timer *); + void (*intr)(struct nvkm_timer *); + u64 (*read)(struct nvkm_timer *); + void (*time)(struct nvkm_timer *, u64 time); + void (*alarm_init)(struct nvkm_timer *, u32 time); + void (*alarm_fini)(struct nvkm_timer *); +}; + +void nvkm_timer_alarm_trigger(struct nvkm_timer *); + +void nv04_timer_fini(struct nvkm_timer *); +void nv04_timer_intr(struct nvkm_timer *); +void nv04_timer_time(struct nvkm_timer *, u64); +u64 nv04_timer_read(struct nvkm_timer *); +void nv04_timer_alarm_init(struct nvkm_timer *, u32); +void nv04_timer_alarm_fini(struct nvkm_timer *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/regsnv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/regsnv04.h new file mode 100644 index 000000000..34a740bc6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/regsnv04.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT */ +#define NV04_PTIMER_INTR_0 0x009100 +#define NV04_PTIMER_INTR_EN_0 0x009140 +#define NV04_PTIMER_NUMERATOR 0x009200 +#define NV04_PTIMER_DENOMINATOR 0x009210 +#define NV04_PTIMER_TIME_0 0x009400 +#define NV04_PTIMER_TIME_1 0x009410 +#define NV04_PTIMER_ALARM_0 0x009420 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/top/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/top/Kbuild new file mode 100644 index 000000000..d5db84519 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/top/Kbuild @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/top/base.o +nvkm-y += nvkm/subdev/top/gk104.o +nvkm-y += nvkm/subdev/top/ga100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c new file mode 100644 index 000000000..28d0789f5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c @@ -0,0 +1,158 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +struct nvkm_top_device * +nvkm_top_device_new(struct nvkm_top *top) +{ + struct nvkm_top_device *info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info) { + info->type = NVKM_SUBDEV_NR; + info->inst = -1; + info->addr = 0; + info->fault = -1; + info->engine = -1; + info->runlist = -1; + info->reset = -1; + info->intr = -1; + list_add_tail(&info->head, &top->device); + } + return info; +} + +u32 +nvkm_top_addr(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + struct nvkm_top *top = device->top; + struct nvkm_top_device *info; + + if (top) { + list_for_each_entry(info, &top->device, head) { + if (info->type == type && info->inst == inst) + return info->addr; + } + } + + return 0; +} + +u32 +nvkm_top_reset(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + struct nvkm_top *top = device->top; + struct nvkm_top_device *info; + + if (top) { + list_for_each_entry(info, &top->device, head) { + if (info->type == type && info->inst == inst && info->reset >= 0) + return BIT(info->reset); + } + } + + return 0; +} + +u32 +nvkm_top_intr_mask(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + struct nvkm_top *top = device->top; + struct nvkm_top_device *info; + + if (top) { + list_for_each_entry(info, &top->device, head) { + if (info->type == type && info->inst == inst && info->intr >= 0) + return BIT(info->intr); + } + } + + return 0; +} + +int +nvkm_top_fault_id(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) +{ + struct nvkm_top *top = device->top; + struct nvkm_top_device *info; + + list_for_each_entry(info, &top->device, head) { + if (info->type == type && info->inst == inst && info->fault >= 0) + return info->fault; + } + + return -ENOENT; +} + +struct nvkm_subdev * +nvkm_top_fault(struct nvkm_device *device, int fault) +{ + struct nvkm_top *top = device->top; + struct nvkm_top_device *info; + + list_for_each_entry(info, &top->device, head) { + if (info->fault == fault) + return nvkm_device_subdev(device, info->type, info->inst); + } + + return NULL; +} + +static int +nvkm_top_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_top *top = nvkm_top(subdev); + return top->func->oneinit(top); +} + +static void * +nvkm_top_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_top *top = nvkm_top(subdev); + struct nvkm_top_device *info, *temp; + + list_for_each_entry_safe(info, temp, &top->device, head) { + list_del(&info->head); + kfree(info); + } + + return top; +} + +static const struct nvkm_subdev_func +nvkm_top = { + .dtor = nvkm_top_dtor, + .oneinit = nvkm_top_oneinit, +}; + +int +nvkm_top_new_(const struct nvkm_top_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_top **ptop) +{ + struct nvkm_top *top; + if (!(top = *ptop = kzalloc(sizeof(*top), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_top, device, type, inst, &top->subdev); + top->func = func; + INIT_LIST_HEAD(&top->device); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/top/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/top/ga100.c new file mode 100644 index 000000000..c982d834c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/top/ga100.c @@ -0,0 +1,108 @@ +/* + * Copyright 2021 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +static int +ga100_top_oneinit(struct nvkm_top *top) +{ + struct nvkm_subdev *subdev = &top->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_top_device *info = NULL; + u32 data, type, inst; + int i, n, size = nvkm_rd32(device, 0x0224fc) >> 20; + + for (i = 0, n = 0; i < size; i++) { + if (!info) { + if (!(info = nvkm_top_device_new(top))) + return -ENOMEM; + type = ~0; + inst = 0; + } + + data = nvkm_rd32(device, 0x022800 + (i * 0x04)); + nvkm_trace(subdev, "%02x: %08x\n", i, data); + if (!data && n == 0) + continue; + + switch (n++) { + case 0: + type = (data & 0x3f000000) >> 24; + inst = (data & 0x000f0000) >> 16; + info->fault = (data & 0x0000007f); + break; + case 1: + info->addr = (data & 0x00fff000); + info->reset = (data & 0x0000001f); + break; + case 2: + info->runlist = (data & 0x00fffc00); + info->engine = (data & 0x00000003); + break; + default: + break; + } + + if (data & 0x80000000) + continue; + n = 0; + + /* Translate engine type to NVKM engine identifier. */ +#define I_(T,I) do { info->type = (T); info->inst = (I); } while(0) +#define O_(T,I) do { WARN_ON(inst); I_(T, I); } while (0) + switch (type) { + case 0x00000000: O_(NVKM_ENGINE_GR , 0); break; + case 0x0000000d: O_(NVKM_ENGINE_SEC2 , 0); break; + case 0x0000000e: I_(NVKM_ENGINE_NVENC , inst); break; + case 0x00000010: I_(NVKM_ENGINE_NVDEC , inst); break; + case 0x00000012: I_(NVKM_SUBDEV_IOCTRL, inst); break; + case 0x00000013: I_(NVKM_ENGINE_CE , inst); break; + case 0x00000014: O_(NVKM_SUBDEV_GSP , 0); break; + case 0x00000015: O_(NVKM_ENGINE_NVJPG , 0); break; + case 0x00000016: O_(NVKM_ENGINE_OFA , 0); break; + case 0x00000017: O_(NVKM_SUBDEV_FLA , 0); break; + break; + default: + break; + } + + nvkm_debug(subdev, "%02x.%d (%8s): addr %06x fault %2d " + "runlist %6x engine %2d reset %2d\n", type, inst, + info->type == NVKM_SUBDEV_NR ? "????????" : nvkm_subdev_type[info->type], + info->addr, info->fault, info->runlist < 0 ? 0 : info->runlist, + info->engine, info->reset); + info = NULL; + } + + return 0; +} + +static const struct nvkm_top_func +ga100_top = { + .oneinit = ga100_top_oneinit, +}; + +int +ga100_top_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_top **ptop) +{ + return nvkm_top_new_(&ga100_top, device, type, inst, ptop); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c new file mode 100644 index 000000000..4dcad97bd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c @@ -0,0 +1,119 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static int +gk104_top_oneinit(struct nvkm_top *top) +{ + struct nvkm_subdev *subdev = &top->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_top_device *info = NULL; + u32 data, type, inst; + int i; + + for (i = 0; i < 64; i++) { + if (!info) { + if (!(info = nvkm_top_device_new(top))) + return -ENOMEM; + type = ~0; + inst = 0; + } + + data = nvkm_rd32(device, 0x022700 + (i * 0x04)); + nvkm_trace(subdev, "%02x: %08x\n", i, data); + switch (data & 0x00000003) { + case 0x00000000: /* NOT_VALID */ + continue; + case 0x00000001: /* DATA */ + inst = (data & 0x3c000000) >> 26; + info->addr = (data & 0x00fff000); + if (data & 0x00000004) + info->fault = (data & 0x000003f8) >> 3; + break; + case 0x00000002: /* ENUM */ + if (data & 0x00000020) + info->engine = (data & 0x3c000000) >> 26; + if (data & 0x00000010) + info->runlist = (data & 0x01e00000) >> 21; + if (data & 0x00000008) + info->intr = (data & 0x000f8000) >> 15; + if (data & 0x00000004) + info->reset = (data & 0x00003e00) >> 9; + break; + case 0x00000003: /* ENGINE_TYPE */ + type = (data & 0x7ffffffc) >> 2; + break; + } + + if (data & 0x80000000) + continue; + + /* Translate engine type to NVKM engine identifier. */ +#define I_(T,I) do { info->type = (T); info->inst = (I); } while(0) +#define O_(T,I) do { WARN_ON(inst); I_(T, I); } while (0) + switch (type) { + case 0x00000000: O_(NVKM_ENGINE_GR , 0); break; + case 0x00000001: O_(NVKM_ENGINE_CE , 0); break; + case 0x00000002: O_(NVKM_ENGINE_CE , 1); break; + case 0x00000003: O_(NVKM_ENGINE_CE , 2); break; + case 0x00000008: O_(NVKM_ENGINE_MSPDEC, 0); break; + case 0x00000009: O_(NVKM_ENGINE_MSPPP , 0); break; + case 0x0000000a: O_(NVKM_ENGINE_MSVLD , 0); break; + case 0x0000000b: O_(NVKM_ENGINE_MSENC , 0); break; + case 0x0000000c: O_(NVKM_ENGINE_VIC , 0); break; + case 0x0000000d: O_(NVKM_ENGINE_SEC2 , 0); break; + case 0x0000000e: I_(NVKM_ENGINE_NVENC , inst); break; + case 0x0000000f: O_(NVKM_ENGINE_NVENC , 1); break; + case 0x00000010: I_(NVKM_ENGINE_NVDEC , inst); break; + case 0x00000012: I_(NVKM_SUBDEV_IOCTRL, inst); break; + case 0x00000013: I_(NVKM_ENGINE_CE , inst); break; + case 0x00000014: O_(NVKM_SUBDEV_GSP , 0); break; + case 0x00000015: O_(NVKM_ENGINE_NVJPG , 0); break; + default: + break; + } + + nvkm_debug(subdev, "%02x.%d (%8s): addr %06x fault %2d " + "engine %2d runlist %2d intr %2d " + "reset %2d\n", type, inst, + info->type == NVKM_SUBDEV_NR ? "????????" : nvkm_subdev_type[info->type], + info->addr, info->fault, info->engine, info->runlist, + info->intr, info->reset); + info = NULL; + } + + return 0; +} + +static const struct nvkm_top_func +gk104_top = { + .oneinit = gk104_top_oneinit, +}; + +int +gk104_top_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_top **ptop) +{ + return nvkm_top_new_(&gk104_top, device, type, inst, ptop); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/top/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/top/priv.h new file mode 100644 index 000000000..8e103a836 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/top/priv.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_TOP_PRIV_H__ +#define __NVKM_TOP_PRIV_H__ +#define nvkm_top(p) container_of((p), struct nvkm_top, subdev) +#include + +struct nvkm_top_func { + int (*oneinit)(struct nvkm_top *); +}; + +int nvkm_top_new_(const struct nvkm_top_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_top **); + +struct nvkm_top_device *nvkm_top_device_new(struct nvkm_top *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild new file mode 100644 index 000000000..523a7cd15 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/volt/base.o +nvkm-y += nvkm/subdev/volt/gpio.o +nvkm-y += nvkm/subdev/volt/nv40.o +nvkm-y += nvkm/subdev/volt/gf100.o +nvkm-y += nvkm/subdev/volt/gf117.o +nvkm-y += nvkm/subdev/volt/gk104.o +nvkm-y += nvkm/subdev/volt/gk20a.o +nvkm-y += nvkm/subdev/volt/gm20b.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c new file mode 100644 index 000000000..a17a6dd8d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c @@ -0,0 +1,328 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +#include +#include +#include +#include + +int +nvkm_volt_get(struct nvkm_volt *volt) +{ + int ret, i; + + if (volt->func->volt_get) + return volt->func->volt_get(volt); + + ret = volt->func->vid_get(volt); + if (ret >= 0) { + for (i = 0; i < volt->vid_nr; i++) { + if (volt->vid[i].vid == ret) + return volt->vid[i].uv; + } + ret = -EINVAL; + } + return ret; +} + +static int +nvkm_volt_set(struct nvkm_volt *volt, u32 uv) +{ + struct nvkm_subdev *subdev = &volt->subdev; + int i, ret = -EINVAL, best_err = volt->max_uv, best = -1; + + if (volt->func->volt_set) + return volt->func->volt_set(volt, uv); + + for (i = 0; i < volt->vid_nr; i++) { + int err = volt->vid[i].uv - uv; + if (err < 0 || err > best_err) + continue; + + best_err = err; + best = i; + if (best_err == 0) + break; + } + + if (best == -1) { + nvkm_error(subdev, "couldn't set %iuv\n", uv); + return ret; + } + + ret = volt->func->vid_set(volt, volt->vid[best].vid); + nvkm_debug(subdev, "set req %duv to %duv: %d\n", uv, + volt->vid[best].uv, ret); + return ret; +} + +int +nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) +{ + struct nvkm_bios *bios = volt->subdev.device->bios; + struct nvbios_vmap_entry info; + u8 ver, len; + u32 vmap; + + vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); + if (vmap) { + if (info.link != 0xff) { + int ret = nvkm_volt_map_min(volt, info.link); + if (ret < 0) + return ret; + info.min += ret; + } + return info.min; + } + + return id ? id * 10000 : -ENODEV; +} + +int +nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) +{ + struct nvkm_bios *bios = volt->subdev.device->bios; + struct nvbios_vmap_entry info; + u8 ver, len; + u32 vmap; + + vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); + if (vmap) { + s64 result; + + if (volt->speedo < 0) + return volt->speedo; + + if (ver == 0x10 || (ver == 0x20 && info.mode == 0)) { + result = div64_s64((s64)info.arg[0], 10); + result += div64_s64((s64)info.arg[1] * volt->speedo, 10); + result += div64_s64((s64)info.arg[2] * volt->speedo * volt->speedo, 100000); + } else if (ver == 0x20) { + switch (info.mode) { + /* 0x0 handled above! */ + case 0x1: + result = ((s64)info.arg[0] * 15625) >> 18; + result += ((s64)info.arg[1] * volt->speedo * 15625) >> 18; + result += ((s64)info.arg[2] * temp * 15625) >> 10; + result += ((s64)info.arg[3] * volt->speedo * temp * 15625) >> 18; + result += ((s64)info.arg[4] * volt->speedo * volt->speedo * 15625) >> 30; + result += ((s64)info.arg[5] * temp * temp * 15625) >> 18; + break; + case 0x3: + result = (info.min + info.max) / 2; + break; + case 0x2: + default: + result = info.min; + break; + } + } else { + return -ENODEV; + } + + result = min(max(result, (s64)info.min), (s64)info.max); + + if (info.link != 0xff) { + int ret = nvkm_volt_map(volt, info.link, temp); + if (ret < 0) + return ret; + result += ret; + } + return result; + } + + return id ? id * 10000 : -ENODEV; +} + +int +nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, u8 temp, + int condition) +{ + int ret; + + if (volt->func->set_id) + return volt->func->set_id(volt, id, condition); + + ret = nvkm_volt_map(volt, id, temp); + if (ret >= 0) { + int prev = nvkm_volt_get(volt); + if (!condition || prev < 0 || + (condition < 0 && ret < prev) || + (condition > 0 && ret > prev)) { + int min = nvkm_volt_map(volt, min_id, temp); + if (min >= 0) + ret = max(min, ret); + ret = nvkm_volt_set(volt, ret); + } else { + ret = 0; + } + } + return ret; +} + +static void +nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) +{ + struct nvkm_subdev *subdev = &bios->subdev; + struct nvbios_volt_entry ivid; + struct nvbios_volt info; + u8 ver, hdr, cnt, len; + u32 data; + int i; + + data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info); + if (data && info.vidmask && info.base && info.step && info.ranged) { + nvkm_debug(subdev, "found ranged based VIDs\n"); + volt->min_uv = info.min; + volt->max_uv = info.max; + for (i = 0; i < info.vidmask + 1; i++) { + if (info.base >= info.min && + info.base <= info.max) { + volt->vid[volt->vid_nr].uv = info.base; + volt->vid[volt->vid_nr].vid = i; + volt->vid_nr++; + } + info.base += info.step; + } + volt->vid_mask = info.vidmask; + } else if (data && info.vidmask && !info.ranged) { + nvkm_debug(subdev, "found entry based VIDs\n"); + volt->min_uv = 0xffffffff; + volt->max_uv = 0; + for (i = 0; i < cnt; i++) { + data = nvbios_volt_entry_parse(bios, i, &ver, &hdr, + &ivid); + if (data) { + volt->vid[volt->vid_nr].uv = ivid.voltage; + volt->vid[volt->vid_nr].vid = ivid.vid; + volt->vid_nr++; + volt->min_uv = min(volt->min_uv, ivid.voltage); + volt->max_uv = max(volt->max_uv, ivid.voltage); + } + } + volt->vid_mask = info.vidmask; + } else if (data && info.type == NVBIOS_VOLT_PWM) { + volt->min_uv = info.base; + volt->max_uv = info.base + info.pwm_range; + } +} + +static int +nvkm_volt_speedo_read(struct nvkm_volt *volt) +{ + if (volt->func->speedo_read) + return volt->func->speedo_read(volt); + return -EINVAL; +} + +static int +nvkm_volt_init(struct nvkm_subdev *subdev) +{ + struct nvkm_volt *volt = nvkm_volt(subdev); + int ret = nvkm_volt_get(volt); + if (ret < 0) { + if (ret != -ENODEV) + nvkm_debug(subdev, "current voltage unknown\n"); + return 0; + } + nvkm_debug(subdev, "current voltage: %duv\n", ret); + return 0; +} + +static int +nvkm_volt_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_volt *volt = nvkm_volt(subdev); + + volt->speedo = nvkm_volt_speedo_read(volt); + if (volt->speedo > 0) + nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo); + + if (volt->func->oneinit) + return volt->func->oneinit(volt); + + return 0; +} + +static void * +nvkm_volt_dtor(struct nvkm_subdev *subdev) +{ + return nvkm_volt(subdev); +} + +static const struct nvkm_subdev_func +nvkm_volt = { + .dtor = nvkm_volt_dtor, + .init = nvkm_volt_init, + .oneinit = nvkm_volt_oneinit, +}; + +void +nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_volt *volt) +{ + struct nvkm_bios *bios = device->bios; + int i; + + nvkm_subdev_ctor(&nvkm_volt, device, type, inst, &volt->subdev); + volt->func = func; + + /* Assuming the non-bios device should build the voltage table later */ + if (bios) { + u8 ver, hdr, cnt, len; + struct nvbios_vmap vmap; + + nvkm_volt_parse_bios(bios, volt); + nvkm_debug(&volt->subdev, "min: %iuv max: %iuv\n", + volt->min_uv, volt->max_uv); + + if (nvbios_vmap_parse(bios, &ver, &hdr, &cnt, &len, &vmap)) { + volt->max0_id = vmap.max0; + volt->max1_id = vmap.max1; + volt->max2_id = vmap.max2; + } else { + volt->max0_id = 0xff; + volt->max1_id = 0xff; + volt->max2_id = 0xff; + } + } + + if (volt->vid_nr) { + for (i = 0; i < volt->vid_nr; i++) { + nvkm_debug(&volt->subdev, "VID %02x: %duv\n", + volt->vid[i].vid, volt->vid[i].uv); + } + } +} + +int +nvkm_volt_new_(const struct nvkm_volt_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_volt **pvolt) +{ + if (!(*pvolt = kzalloc(sizeof(**pvolt), GFP_KERNEL))) + return -ENOMEM; + nvkm_volt_ctor(func, device, type, inst, *pvolt); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c new file mode 100644 index 000000000..b47a1c081 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf100.c @@ -0,0 +1,71 @@ +/* + * Copyright 2016 Karol Herbst + * + * 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: Karol Herbst + */ +#include "priv.h" + +#include + +static int +gf100_volt_speedo_read(struct nvkm_volt *volt) +{ + struct nvkm_device *device = volt->subdev.device; + struct nvkm_fuse *fuse = device->fuse; + + if (!fuse) + return -EINVAL; + + return nvkm_fuse_read(fuse, 0x1cc); +} + +int +gf100_volt_oneinit(struct nvkm_volt *volt) +{ + struct nvkm_subdev *subdev = &volt->subdev; + if (volt->speedo <= 0) + nvkm_error(subdev, "couldn't find speedo value, volting not " + "possible\n"); + return 0; +} + +static const struct nvkm_volt_func +gf100_volt = { + .oneinit = gf100_volt_oneinit, + .vid_get = nvkm_voltgpio_get, + .vid_set = nvkm_voltgpio_set, + .speedo_read = gf100_volt_speedo_read, +}; + +int +gf100_volt_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_volt **pvolt) +{ + struct nvkm_volt *volt; + int ret; + + ret = nvkm_volt_new_(&gf100_volt, device, type, inst, &volt); + *pvolt = volt; + if (ret) + return ret; + + return nvkm_voltgpio_init(volt); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf117.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf117.c new file mode 100644 index 000000000..03c8a2c29 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gf117.c @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Ilia Mirkin + * + * 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: Ilia Mirkin + */ +#include "priv.h" + +#include + +static int +gf117_volt_speedo_read(struct nvkm_volt *volt) +{ + struct nvkm_device *device = volt->subdev.device; + struct nvkm_fuse *fuse = device->fuse; + + if (!fuse) + return -EINVAL; + + return nvkm_fuse_read(fuse, 0x3a8); +} + +static const struct nvkm_volt_func +gf117_volt = { + .oneinit = gf100_volt_oneinit, + .vid_get = nvkm_voltgpio_get, + .vid_set = nvkm_voltgpio_set, + .speedo_read = gf117_volt_speedo_read, +}; + +int +gf117_volt_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_volt **pvolt) +{ + struct nvkm_volt *volt; + int ret; + + ret = nvkm_volt_new_(&gf117_volt, device, type, inst, &volt); + *pvolt = volt; + if (ret) + return ret; + + return nvkm_voltgpio_init(volt); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c new file mode 100644 index 000000000..d1ce4309c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c @@ -0,0 +1,141 @@ +/* + * Copyright 2015 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +#include +#include +#include +#include +#include + +#define gk104_volt(p) container_of((p), struct gk104_volt, base) +struct gk104_volt { + struct nvkm_volt base; + struct nvbios_volt bios; +}; + +static int +gk104_volt_get(struct nvkm_volt *base) +{ + struct nvbios_volt *bios = &gk104_volt(base)->bios; + struct nvkm_device *device = base->subdev.device; + u32 div, duty; + + div = nvkm_rd32(device, 0x20340); + duty = nvkm_rd32(device, 0x20344); + + return bios->base + bios->pwm_range * duty / div; +} + +static int +gk104_volt_set(struct nvkm_volt *base, u32 uv) +{ + struct nvbios_volt *bios = &gk104_volt(base)->bios; + struct nvkm_device *device = base->subdev.device; + u32 div, duty; + + /* the blob uses this crystal frequency, let's use it too. */ + div = 27648000 / bios->pwm_freq; + duty = DIV_ROUND_UP((uv - bios->base) * div, bios->pwm_range); + + nvkm_wr32(device, 0x20340, div); + nvkm_wr32(device, 0x20344, 0x80000000 | duty); + + return 0; +} + +static int +gk104_volt_speedo_read(struct nvkm_volt *volt) +{ + struct nvkm_device *device = volt->subdev.device; + struct nvkm_fuse *fuse = device->fuse; + int ret; + + if (!fuse) + return -EINVAL; + + nvkm_wr32(device, 0x122634, 0x0); + ret = nvkm_fuse_read(fuse, 0x3a8); + nvkm_wr32(device, 0x122634, 0x41); + return ret; +} + +static const struct nvkm_volt_func +gk104_volt_pwm = { + .oneinit = gf100_volt_oneinit, + .volt_get = gk104_volt_get, + .volt_set = gk104_volt_set, + .speedo_read = gk104_volt_speedo_read, +}, gk104_volt_gpio = { + .oneinit = gf100_volt_oneinit, + .vid_get = nvkm_voltgpio_get, + .vid_set = nvkm_voltgpio_set, + .speedo_read = gk104_volt_speedo_read, +}; + +int +gk104_volt_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_volt **pvolt) +{ + const struct nvkm_volt_func *volt_func = &gk104_volt_gpio; + struct dcb_gpio_func gpio; + struct nvbios_volt bios; + struct gk104_volt *volt; + u8 ver, hdr, cnt, len; + const char *mode; + + if (!nvbios_volt_parse(device->bios, &ver, &hdr, &cnt, &len, &bios)) + return 0; + + if (!nvkm_gpio_find(device->gpio, 0, DCB_GPIO_VID_PWM, 0xff, &gpio) && + bios.type == NVBIOS_VOLT_PWM) { + volt_func = &gk104_volt_pwm; + } + + if (!(volt = kzalloc(sizeof(*volt), GFP_KERNEL))) + return -ENOMEM; + nvkm_volt_ctor(volt_func, device, type, inst, &volt->base); + *pvolt = &volt->base; + volt->bios = bios; + + /* now that we have a subdev, we can show an error if we found through + * the voltage table that we were supposed to use the PWN mode but we + * did not find the right GPIO for it. + */ + if (bios.type == NVBIOS_VOLT_PWM && volt_func != &gk104_volt_pwm) { + nvkm_error(&volt->base.subdev, + "Type mismatch between the voltage table type and " + "the GPIO table. Fallback to GPIO mode.\n"); + } + + if (volt_func == &gk104_volt_gpio) { + nvkm_voltgpio_init(&volt->base); + mode = "GPIO"; + } else + mode = "PWM"; + + nvkm_debug(&volt->base.subdev, "Using %s mode\n", mode); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c new file mode 100644 index 000000000..8c2faa964 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define gk20a_volt(p) container_of((p), struct gk20a_volt, base) +#include "priv.h" + +#include + +#include "gk20a.h" + +static const struct cvb_coef gk20a_cvb_coef[] = { + /* MHz, c0, c1, c2, c3, c4, c5 */ + /* 72 */ { 1209886, -36468, 515, 417, -13123, 203}, + /* 108 */ { 1130804, -27659, 296, 298, -10834, 221}, + /* 180 */ { 1162871, -27110, 247, 238, -10681, 268}, + /* 252 */ { 1220458, -28654, 247, 179, -10376, 298}, + /* 324 */ { 1280953, -30204, 247, 119, -9766, 304}, + /* 396 */ { 1344547, -31777, 247, 119, -8545, 292}, + /* 468 */ { 1420168, -34227, 269, 60, -7172, 256}, + /* 540 */ { 1490757, -35955, 274, 60, -5188, 197}, + /* 612 */ { 1599112, -42583, 398, 0, -1831, 119}, + /* 648 */ { 1366986, -16459, -274, 0, -3204, 72}, + /* 684 */ { 1391884, -17078, -274, -60, -1526, 30}, + /* 708 */ { 1415522, -17497, -274, -60, -458, 0}, + /* 756 */ { 1464061, -18331, -274, -119, 1831, -72}, + /* 804 */ { 1524225, -20064, -254, -119, 4272, -155}, + /* 852 */ { 1608418, -21643, -269, 0, 763, -48}, +}; + +/** + * cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) + */ +static inline int +gk20a_volt_get_cvb_voltage(int speedo, int s_scale, const struct cvb_coef *coef) +{ + int mv; + + mv = DIV_ROUND_CLOSEST(coef->c2 * speedo, s_scale); + mv = DIV_ROUND_CLOSEST((mv + coef->c1) * speedo, s_scale) + coef->c0; + return mv; +} + +/** + * cvb_t_mv = + * ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) + + * ((c3 * speedo / s_scale + c4 + c5 * T / t_scale) * T / t_scale) + */ +static inline int +gk20a_volt_get_cvb_t_voltage(int speedo, int temp, int s_scale, int t_scale, + const struct cvb_coef *coef) +{ + int cvb_mv, mv; + + cvb_mv = gk20a_volt_get_cvb_voltage(speedo, s_scale, coef); + + mv = DIV_ROUND_CLOSEST(coef->c3 * speedo, s_scale) + coef->c4 + + DIV_ROUND_CLOSEST(coef->c5 * temp, t_scale); + mv = DIV_ROUND_CLOSEST(mv * temp, t_scale) + cvb_mv; + return mv; +} + +static int +gk20a_volt_calc_voltage(const struct cvb_coef *coef, int speedo) +{ + static const int v_scale = 1000; + int mv; + + mv = gk20a_volt_get_cvb_t_voltage(speedo, -10, 100, 10, coef); + mv = DIV_ROUND_UP(mv, v_scale); + + return mv * 1000; +} + +static int +gk20a_volt_vid_get(struct nvkm_volt *base) +{ + struct gk20a_volt *volt = gk20a_volt(base); + int i, uv; + + uv = regulator_get_voltage(volt->vdd); + + for (i = 0; i < volt->base.vid_nr; i++) + if (volt->base.vid[i].uv >= uv) + return i; + + return -EINVAL; +} + +static int +gk20a_volt_vid_set(struct nvkm_volt *base, u8 vid) +{ + struct gk20a_volt *volt = gk20a_volt(base); + struct nvkm_subdev *subdev = &volt->base.subdev; + + nvkm_debug(subdev, "set voltage as %duv\n", volt->base.vid[vid].uv); + return regulator_set_voltage(volt->vdd, volt->base.vid[vid].uv, 1200000); +} + +static int +gk20a_volt_set_id(struct nvkm_volt *base, u8 id, int condition) +{ + struct gk20a_volt *volt = gk20a_volt(base); + struct nvkm_subdev *subdev = &volt->base.subdev; + int prev_uv = regulator_get_voltage(volt->vdd); + int target_uv = volt->base.vid[id].uv; + int ret; + + nvkm_debug(subdev, "prev=%d, target=%d, condition=%d\n", + prev_uv, target_uv, condition); + if (!condition || + (condition < 0 && target_uv < prev_uv) || + (condition > 0 && target_uv > prev_uv)) { + ret = gk20a_volt_vid_set(&volt->base, volt->base.vid[id].vid); + } else { + ret = 0; + } + + return ret; +} + +static const struct nvkm_volt_func +gk20a_volt = { + .vid_get = gk20a_volt_vid_get, + .vid_set = gk20a_volt_vid_set, + .set_id = gk20a_volt_set_id, +}; + +int +gk20a_volt_ctor(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + const struct cvb_coef *coefs, int nb_coefs, + int vmin, struct gk20a_volt *volt) +{ + struct nvkm_device_tegra *tdev = device->func->tegra(device); + int i, uv; + + nvkm_volt_ctor(&gk20a_volt, device, type, inst, &volt->base); + + uv = regulator_get_voltage(tdev->vdd); + nvkm_debug(&volt->base.subdev, "the default voltage is %duV\n", uv); + + volt->vdd = tdev->vdd; + + volt->base.vid_nr = nb_coefs; + for (i = 0; i < volt->base.vid_nr; i++) { + volt->base.vid[i].vid = i; + volt->base.vid[i].uv = max( + gk20a_volt_calc_voltage(&coefs[i], tdev->gpu_speedo), + vmin); + nvkm_debug(&volt->base.subdev, "%2d: vid=%d, uv=%d\n", i, + volt->base.vid[i].vid, volt->base.vid[i].uv); + } + + return 0; +} + +int +gk20a_volt_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_volt **pvolt) +{ + struct gk20a_volt *volt; + + volt = kzalloc(sizeof(*volt), GFP_KERNEL); + if (!volt) + return -ENOMEM; + *pvolt = &volt->base; + + return gk20a_volt_ctor(device, type, inst, gk20a_cvb_coef, + ARRAY_SIZE(gk20a_cvb_coef), 0, volt); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.h b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.h new file mode 100644 index 000000000..01f8a5fcf --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __GK20A_VOLT_H__ +#define __GK20A_VOLT_H__ + +struct cvb_coef { + int c0; + int c1; + int c2; + int c3; + int c4; + int c5; +}; + +struct gk20a_volt { + struct nvkm_volt base; + struct regulator *vdd; +}; + +int gk20a_volt_ctor(struct nvkm_device *device, enum nvkm_subdev_type, int, + const struct cvb_coef *coefs, int nb_coefs, + int vmin, struct gk20a_volt *volt); + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gm20b.c new file mode 100644 index 000000000..c2e9694d3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gm20b.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "priv.h" +#include "gk20a.h" + +#include + +static const struct cvb_coef gm20b_cvb_coef[] = { + /* KHz, c0, c1, c2 */ + /* 76800 */ { 1786666, -85625, 1632 }, + /* 153600 */ { 1846729, -87525, 1632 }, + /* 230400 */ { 1910480, -89425, 1632 }, + /* 307200 */ { 1977920, -91325, 1632 }, + /* 384000 */ { 2049049, -93215, 1632 }, + /* 460800 */ { 2122872, -95095, 1632 }, + /* 537600 */ { 2201331, -96985, 1632 }, + /* 614400 */ { 2283479, -98885, 1632 }, + /* 691200 */ { 2369315, -100785, 1632 }, + /* 768000 */ { 2458841, -102685, 1632 }, + /* 844800 */ { 2550821, -104555, 1632 }, + /* 921600 */ { 2647676, -106455, 1632 }, +}; + +static const struct cvb_coef gm20b_na_cvb_coef[] = { + /* KHz, c0, c1, c2, c3, c4, c5 */ + /* 76800 */ { 814294, 8144, -940, 808, -21583, 226 }, + /* 153600 */ { 856185, 8144, -940, 808, -21583, 226 }, + /* 230400 */ { 898077, 8144, -940, 808, -21583, 226 }, + /* 307200 */ { 939968, 8144, -940, 808, -21583, 226 }, + /* 384000 */ { 981860, 8144, -940, 808, -21583, 226 }, + /* 460800 */ { 1023751, 8144, -940, 808, -21583, 226 }, + /* 537600 */ { 1065642, 8144, -940, 808, -21583, 226 }, + /* 614400 */ { 1107534, 8144, -940, 808, -21583, 226 }, + /* 691200 */ { 1149425, 8144, -940, 808, -21583, 226 }, + /* 768000 */ { 1191317, 8144, -940, 808, -21583, 226 }, + /* 844800 */ { 1233208, 8144, -940, 808, -21583, 226 }, + /* 921600 */ { 1275100, 8144, -940, 808, -21583, 226 }, + /* 998400 */ { 1316991, 8144, -940, 808, -21583, 226 }, +}; + +static const u32 speedo_to_vmin[] = { + /* 0, 1, 2, 3, 4, */ + 950000, 840000, 818750, 840000, 810000, +}; + +int +gm20b_volt_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_volt **pvolt) +{ + struct nvkm_device_tegra *tdev = device->func->tegra(device); + struct gk20a_volt *volt; + u32 vmin; + + if (tdev->gpu_speedo_id >= ARRAY_SIZE(speedo_to_vmin)) { + nvdev_error(device, "unsupported speedo %d\n", + tdev->gpu_speedo_id); + return -EINVAL; + } + + volt = kzalloc(sizeof(*volt), GFP_KERNEL); + if (!volt) + return -ENOMEM; + *pvolt = &volt->base; + + vmin = speedo_to_vmin[tdev->gpu_speedo_id]; + + if (tdev->gpu_speedo_id >= 1) + return gk20a_volt_ctor(device, type, inst, gm20b_na_cvb_coef, + ARRAY_SIZE(gm20b_na_cvb_coef), vmin, volt); + else + return gk20a_volt_ctor(device, type, inst, gm20b_cvb_coef, + ARRAY_SIZE(gm20b_cvb_coef), vmin, volt); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c new file mode 100644 index 000000000..443c031b9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c @@ -0,0 +1,98 @@ +/* + * 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 +#include +#include +#include +#include "priv.h" + +static const u8 tags[] = { + DCB_GPIO_VID0, DCB_GPIO_VID1, DCB_GPIO_VID2, DCB_GPIO_VID3, + DCB_GPIO_VID4, DCB_GPIO_VID5, DCB_GPIO_VID6, DCB_GPIO_VID7, +}; + +int +nvkm_voltgpio_get(struct nvkm_volt *volt) +{ + struct nvkm_gpio *gpio = volt->subdev.device->gpio; + u8 vid = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(tags); i++) { + if (volt->vid_mask & (1 << i)) { + int ret = nvkm_gpio_get(gpio, 0, tags[i], 0xff); + if (ret < 0) + return ret; + vid |= ret << i; + } + } + + return vid; +} + +int +nvkm_voltgpio_set(struct nvkm_volt *volt, u8 vid) +{ + struct nvkm_gpio *gpio = volt->subdev.device->gpio; + int i; + + for (i = 0; i < ARRAY_SIZE(tags); i++, vid >>= 1) { + if (volt->vid_mask & (1 << i)) { + int ret = nvkm_gpio_set(gpio, 0, tags[i], 0xff, vid & 1); + if (ret < 0) + return ret; + } + } + + return 0; +} + +int +nvkm_voltgpio_init(struct nvkm_volt *volt) +{ + struct nvkm_subdev *subdev = &volt->subdev; + struct nvkm_gpio *gpio = subdev->device->gpio; + struct dcb_gpio_func func; + int i; + + /* check we have gpio function info for each vid bit. on some + * boards (ie. nvs295) the vid mask has more bits than there + * are valid gpio functions... from traces, nvidia appear to + * just touch the existing ones, so let's mask off the invalid + * bits and continue with life + */ + for (i = 0; i < ARRAY_SIZE(tags); i++) { + if (volt->vid_mask & (1 << i)) { + int ret = nvkm_gpio_find(gpio, 0, tags[i], 0xff, &func); + if (ret) { + if (ret != -ENOENT) + return ret; + nvkm_debug(subdev, "VID bit %d has no GPIO\n", i); + volt->vid_mask &= ~(1 << i); + } + } + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c new file mode 100644 index 000000000..d6a587d60 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c @@ -0,0 +1,45 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "priv.h" + +static const struct nvkm_volt_func +nv40_volt = { + .vid_get = nvkm_voltgpio_get, + .vid_set = nvkm_voltgpio_set, +}; + +int +nv40_volt_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_volt **pvolt) +{ + struct nvkm_volt *volt; + int ret; + + ret = nvkm_volt_new_(&nv40_volt, device, type, inst, &volt); + *pvolt = volt; + if (ret) + return ret; + + return nvkm_voltgpio_init(volt); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h new file mode 100644 index 000000000..24e2d16d1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_VOLT_PRIV_H__ +#define __NVKM_VOLT_PRIV_H__ +#define nvkm_volt(p) container_of((p), struct nvkm_volt, subdev) +#include + +void nvkm_volt_ctor(const struct nvkm_volt_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_volt *); +int nvkm_volt_new_(const struct nvkm_volt_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_volt **); + +struct nvkm_volt_func { + int (*oneinit)(struct nvkm_volt *); + int (*volt_get)(struct nvkm_volt *); + int (*volt_set)(struct nvkm_volt *, u32 uv); + int (*vid_get)(struct nvkm_volt *); + int (*vid_set)(struct nvkm_volt *, u8 vid); + int (*set_id)(struct nvkm_volt *, u8 id, int condition); + int (*speedo_read)(struct nvkm_volt *); +}; + +int nvkm_voltgpio_init(struct nvkm_volt *); +int nvkm_voltgpio_get(struct nvkm_volt *); +int nvkm_voltgpio_set(struct nvkm_volt *, u8); + +int nvkm_voltpwm_init(struct nvkm_volt *volt); +int nvkm_voltpwm_get(struct nvkm_volt *volt); +int nvkm_voltpwm_set(struct nvkm_volt *volt, u32 uv); + +int gf100_volt_oneinit(struct nvkm_volt *); +#endif -- cgit v1.2.3